Configure PHP-FPM with NGINX reverse proxy and SSL certificates

Intermediate 25 min May 14, 2026 28 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up PHP-FPM with NGINX as a reverse proxy and secure it with Let's Encrypt SSL certificates. This configuration provides high performance for PHP applications with proper process isolation and automatic HTTPS.

Prerequisites

  • Root or sudo access
  • Domain name pointing to your server
  • Open ports 80 and 443

What this solves

PHP-FPM (FastCGI Process Manager) with NGINX reverse proxy creates a high-performance setup for PHP applications. This configuration separates web serving from PHP processing, improving security and performance compared to traditional Apache mod_php setups.

You need this when running PHP applications that require better performance, process isolation, or when you want to serve static files efficiently while processing PHP requests separately.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of all components.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install NGINX and PHP-FPM

Install NGINX web server and PHP-FPM with essential PHP extensions for web applications.

sudo apt install -y nginx php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip
sudo dnf install -y nginx php-fpm php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip

Install Certbot for SSL certificates

Install Certbot to automatically obtain and manage Let's Encrypt SSL certificates.

sudo apt install -y certbot python3-certbot-nginx
sudo dnf install -y certbot python3-certbot-nginx

Configure PHP-FPM pool

Create a dedicated PHP-FPM pool for your application with optimized settings for performance and security.

sudo cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf.backup
[www]
user = www-data
group = www-data

listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/sessions
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache

security.limit_extensions = .php .php3 .php4 .php5 .php7 .php8

Configure NGINX server block

Create an NGINX server block that will act as a reverse proxy to PHP-FPM for PHP requests while serving static files directly.

sudo mkdir -p /var/www/example.com/public
sudo chown -R www-data:www-data /var/www/example.com
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com/public;
    index index.php index.html index.htm;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

    # Handle static files
    location ~* \.(css|gif|ico|jpeg|jpg|js|png|svg|webp|woff|woff2|ttf|eot)$ {
        expires 1M;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # PHP-FPM reverse proxy
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        
        # Security settings
        fastcgi_param HTTP_PROXY "";
        fastcgi_read_timeout 300;
    }

    # Deny access to sensitive files
    location ~ /\. {
        deny all;
    }

    location ~ ~$ {
        deny all;
    }

    # Main location block
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Logging
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;
}

Enable the site and test configuration

Enable the NGINX site and test the configuration for syntax errors before restarting services.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t

Create a test PHP file

Create a simple PHP file to test that PHP-FPM is working correctly with NGINX.

sudo chown www-data:www-data /var/www/example.com/public/info.php
sudo chmod 644 /var/www/example.com/public/info.php

Start and enable services

Start both PHP-FPM and NGINX services and enable them to start automatically on boot.

sudo systemctl enable --now php8.3-fpm
sudo systemctl enable --now nginx
sudo systemctl status php8.3-fpm
sudo systemctl status nginx

Obtain SSL certificate

Use Certbot to automatically obtain and configure SSL certificates from Let's Encrypt.

sudo certbot --nginx -d example.com -d www.example.com

Follow the prompts to enter your email and agree to terms of service. Certbot will automatically modify your NGINX configuration to include SSL settings.

Configure automatic certificate renewal

Set up automatic renewal for SSL certificates to ensure they don't expire.

sudo systemctl enable --now certbot.timer
sudo systemctl status certbot.timer

Test the renewal process to ensure it works correctly.

sudo certbot renew --dry-run

Performance optimization

Optimize PHP-FPM settings

Fine-tune PHP-FPM for better performance based on your server's resources.

memory_limit = 256M
max_execution_time = 300
max_input_vars = 3000
upload_max_filesize = 64M
post_max_size = 64M
max_file_uploads = 20

; OpCache settings
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=2
opcache.fast_shutdown=1

Configure NGINX performance settings

Optimize NGINX for better performance with caching and connection handling.

user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    multi_accept on;
    worker_connections 1024;
    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 16M;

    # MIME
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # Gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    include /etc/nginx/sites-enabled/*;
}

Security hardening

Secure PHP-FPM configuration

Apply additional security settings to PHP-FPM to prevent common vulnerabilities.

; Add these security settings to your existing pool configuration
php_admin_flag[allow_url_fopen] = off
php_admin_flag[allow_url_include] = off
php_admin_flag[enable_dl] = off
php_admin_flag[expose_php] = off
php_admin_flag[display_errors] = off
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen

Configure firewall rules

Set up UFW firewall to allow only necessary ports for web traffic.

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

Set up log monitoring

Configure log rotation and basic monitoring for PHP-FPM and NGINX logs.

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0644 www-data adm
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 cat /var/run/nginx.pid
        fi
    endscript
}

/var/log/fpm-php.www.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0644 www-data adm
    postrotate
        systemctl reload php8.3-fpm
    endscript
}

Restart services with new configuration

Apply all configuration changes by restarting both services.

sudo systemctl restart php8.3-fpm
sudo systemctl restart nginx

Verify your setup

Test that PHP-FPM is working correctly with NGINX and SSL is properly configured.

sudo systemctl status php8.3-fpm
sudo systemctl status nginx
curl -I https://example.com
curl -k https://example.com/info.php | grep "PHP Version"

Check SSL certificate status and configuration.

sudo certbot certificates
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates

Monitor PHP-FPM pool status and performance.

sudo tail -f /var/log/fpm-php.www.log
sudo tail -f /var/log/nginx/example.com.access.log
Remember to remove the test file. Delete /var/www/example.com/public/info.php after testing as it exposes system information.

Common issues

Symptom Cause Fix
502 Bad Gateway error PHP-FPM not running or socket permissions sudo systemctl restart php8.3-fpm and check socket permissions
PHP files download instead of executing NGINX not configured to pass PHP to FPM Check fastcgi_pass directive points to correct socket
SSL certificate not renewing Certbot timer not running sudo systemctl enable --now certbot.timer
Permission denied errors Wrong file ownership or permissions sudo chown -R www-data:www-data /var/www/example.com
High memory usage Too many PHP-FPM processes Adjust pm.max_children in pool configuration
Slow PHP execution OpCache disabled or misconfigured Enable and tune OpCache settings in php.ini

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.