Install Nginx dengan PHP-FPM dan Userdir

Berikut merupakan cara untuk menginstall webserver Nginx dengan PHP-FPM dan Userdir. Sebelum memulai pastikan Anda sudah memilki 1 server CentOS dengan RAM minimal 1GB.

Instalasi Nginx

1. Tambahkan Repository Nginx

Untuk menambahkan repository resmi Nginx, ikuti panduan dari situs resmi https://nginx.org/en/linux_packages.html

Pastikan Anda memilih panduan sesuai dengan distribusi Linux yang digunakan (misalnya CentOS, RHEL, Ubuntu, atau Debian). Panduan tersebut mencakup langkah-langkah untuk:

  • Menambahkan file repository Nginx.
  • Mengimpor GPG key untuk verifikasi paket.
  • Memilih antara versi stable atau mainline.

2. Instalasi Nginx

Untuk CentOS/RHEL, jalankan perintah berikut:

yum -y install nginx

3. Instalasi Nginx (untuk Ubuntu/Debian)

Jika Anda menggunakan Ubuntu atau Debian, jalankan:

apt -y install nginx \
  libnginx-mod-http-auth-pam \
  libnginx-mod-http-brotli-filter \
  libnginx-mod-http-brotli-static \
  libnginx-mod-http-cache-purge \
  libnginx-mod-http-dav-ext \
  libnginx-mod-http-echo \
  libnginx-mod-http-fancyindex \
  libnginx-mod-http-geoip \
  libnginx-mod-http-geoip2 \
  libnginx-mod-http-headers-more-filter \
  libnginx-mod-http-image-filter \
  libnginx-mod-http-lua \
  libnginx-mod-http-ndk \
  libnginx-mod-http-ndk-dev \
  libnginx-mod-http-perl \
  libnginx-mod-http-subs-filter \
  libnginx-mod-http-uploadprogress \
  libnginx-mod-http-upstream-fair \
  libnginx-mod-http-xslt-filter \
  libnginx-mod-mail \
  libnginx-mod-nchan \
  libnginx-mod-rtmp \
  libnginx-mod-stream \
  libnginx-mod-stream-geoip \
  libnginx-mod-stream-geoip2

4. Buat User web1

Buat user baru bernama web1

useradd -m web1

5. Aktifkan Fitur Userdir

Edit file konfigurasi Nginx, misalnya /etc/nginx/conf.d/default.conf, dan tambahkan blok berikut ke dalam bagian server { ... }:

location ~ ^/~([^/]+)(/.*)?$ {
    alias /home/$1/public_html$2;
    index index.html index.htm;
    autoindex on;
}

Pastikan direktori /home/USERNAME/public_html memiliki permission yang tepat agar bisa diakses oleh Nginx.

Instalasi PHP

1. Tambahkan Repository Remi

Untuk mendapatkan versi PHP yang lebih lengkap dan terbaru, instal repo Remi terlebih dahulu:

yum -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm

2. Instal PHP 7.4 dan Modul yang Dibutuhkan

Setelah repo Remi terpasang, instal PHP 7.4 beserta modul-modul yang umum digunakan:

yum -y install \
  php74.x86_64 \
  php74-php-bcmath.x86_64 \
  php74-php-brotli.x86_64 \
  php74-php-fpm.x86_64 \
  php74-php-gd.x86_64 \
  php74-php-imap.x86_64 \
  php74-php-intl.x86_64 \
  php74-php-ioncube-loader.x86_64 \
  php74-php-json.x86_64 \
  php74-php-mbstring.x86_64 \
  php74-php-mysqlnd.x86_64 \
  php74-php-opcache.x86_64 \
  php74-php-pecl-imagick.x86_64 \
  php74-php-pecl-zip.x86_64 \
  php74-php-sodium.x86_64 \
  php74-php-xml.x86_64 \
  php74-php-xmlrpc.x86_64

3. Buat Pool FPM untuk User web1

Buat file konfigurasi baru untuk PHP-FPM pool web1:

nano /etc/opt/remi/php74/php-fpm.d/web1.conf

Isi file tersebut dengan konfigurasi berikut:

[web1]
user = web1
group = web1
catch_workers_output = yes
chdir = /home/web1/public_html

listen = /var/opt/remi/php74/run/php-fpm/web1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
listen.acl_users = web1,www-data
listen.allowed_clients = 127.0.0.1
listen.backlog = 32768

request_slowlog_timeout = 5s
slowlog = /home/web1/logs/php.slow.log

pm = ondemand
pm.max_children = 50
pm.max_requests = 200
pm.process_idle_timeout = 10s
pm.start_servers = 1
pm.max_spare_servers = 1
pm.min_spare_servers = 1

pm.status_path = /status
ping.path = /ping
request_terminate_timeout = 300
security.limit_extensions = .phtml .php .php3 .php4 .php5 .php6 .php7 .php8

; PHP configuration
php_admin_flag[allow_url_fopen] = on
php_admin_flag[log_errors] = on
; abused php functions
; php_admin_value[disable_functions] = show_source, system, shell_exec, passthru, exec, popen, proc_open
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_value[short_open_tag] = on
php_admin_value[sys_temp_dir] = "/home/web1/tmp"
php_admin_value[upload_tmp_dir] = "/home/web1/tmp"
php_admin_value[max_input_vars] = 10000
php_admin_value[doc_root] = "/home/web1/public_html"
php_admin_value[error_log] = /home/web1/logs/php.error.log

php_value[error_reporting] = E_ALL & ~E_NOTICE
php_value[max_execution_time] = 300
php_value[max_input_time] = 300
php_value[memory_limit] = 512M
;php_value[open_basedir] = "/home/web1/:/tmp/:/var/www/:/usr/share/php/:/var/run/nginx-cache/:/dev/urandom:/dev/shm:/var/lib/php/sessions/"
php_value[session.save_handler] = files
php_value[session.save_path] = "/home/web1/tmp"
php_value[date.timezone] = "Asia/Jakarta"
php_value[post_max_size] = 256M
php_value[upload_max_filesize] = 128M

env[TMPDIR] = "/home/web1/tmp"

Konfigurasi Virtual Host

1. Login sebagai User web1

su - web1

2. Buat Direktori Publik dan Atur Permission

Jalankan perintah berikut sebagai user web1 untuk membuat direktori yang dibutuhkan:

mkdir -p ~/public_html ~/logs ~/tmp
chmod 711 /home/web1/

Penjelasan:

  • public_html — untuk menyimpan file website.
  • logs — untuk menyimpan file log PHP.
  • tmp — untuk keperluan temporary file (upload, session, dsb).
  • chmod 711 /home/web1/ — memastikan Nginx dapat mengakses direktori home, dan membatasi isi direktori supaya tidak bisa dilihat oleh pengguna lain.

3. Buat File index.php untuk Pengujian

nano ~/public_html/index.php

Isi dengan:

<?php phpinfo(); ?>

4. Buat Konfigurasi Virtual Host Nginx

Buat file baru:

nano /etc/nginx/conf.d/web1.conf

Isi dengan konfigurasi berikut:

server {
    listen       80;
    server_name  web1.example.com;

    root   /home/web1/public_html;
    index "index.html" "index.cgi" "index.pl" "index.php" "index.xhtml" "index.htm" "index.shtml";

    client_max_body_size 128m;

    access_log  /var/log/nginx/web1.access.log rt_cache;
    error_log   /var/log/nginx/web1.error.log;

    disable_symlinks if_not_owner from=/home/web1/public_html;

    # SEO-friendly permalink fallback
    set $sef_entry_point /;
    if ($uri ~* "^/") {
        set $sef_entry_point "/index.php?$args";
    }

    location @wpt_permalinks_fallback {
        try_files $uri $sef_entry_point;
    }

    error_page 404 = @wpt_permalinks_fallback;
    error_page 405 = @wpt_permalinks_fallback;

    include location.conf;

    location ~ \.php(/.*)?$ {
        try_files $uri $fastcgi_script_name =404;
        fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass unix:/var/opt/remi/php74/run/php-fpm/web1.sock;
        fastcgi_read_timeout 300;
        fastcgi_buffer_size 64k;
        fastcgi_buffers 32 32k;
        include /etc/nginx/fastcgi.conf;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

5. Buat File fastcgi.conf (Jika Belum Ada)

Jika file /etc/nginx/fastcgi.conf belum tersedia (misalnya di beberapa distribusi minimalis), Anda dapat membuatnya secara manual:

nano /etc/nginx/fastcgi.conf

Isi dengan konfigurasi berikut:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# Untuk PHP dengan opsi build --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

# To fix CGI application vulnerability - https://httpoxy.org
fastcgi_param  HTTP_PROXY         "";

# Memastikan HTTP_HOST tetap terbaca saat menggunakan HTTP/3 (QUIC)
fastcgi_param  HTTP_HOST          $host;

6. Buat File location.conf

File ini berisi konfigurasi lokasi tambahan untuk optimasi cache, keamanan, dan dukungan Let’s Encrypt. Jalankan perintah berikut:

nano /etc/nginx/location.conf

Isi dengan konfigurasi berikut:

# Basic locations files
location = /favicon.ico {
    try_files /wp-content/uploads/fbrfg/favicon.ico $uri $uri/ /index.php?$args @empty_gif;
    access_log off;
    log_not_found off;
    expires max;
}

# Dummy GIF response
location @empty_gif {
    empty_gif;
}

# Cache untuk file statis
location ~* \.(ogg|ogv|svg|svgz|eot|otf|woff|woff2|ttf|m4a|mp4|rss|atom|jpe?g|gif|cur|heic|png|tiff|ico|webm|mp3|aac|tgz|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf|swf|webp|json|webmanifest|cast)$ {
    more_set_headers 'Access-Control-Allow-Origin: *';
    more_set_headers 'Cache-Control: public, no-transform';
    access_log off;
    log_not_found off;
    expires max;
}

# Cache untuk CSS & JS
location ~* \.(css(\.map)?|js(\.map)?)$ {
    more_set_headers 'Access-Control-Allow-Origin: *';
    more_set_headers 'Cache-Control: public, no-transform';
    access_log off;
    log_not_found off;
    expires 1y;
}

# Tolak akses file tersembunyi (kecuali .well-known)
location ~ /\.(?!well-known\/) {
    deny all;
}

# Validasi Let's Encrypt
location /.well-known/acme-challenge/ {
    alias /var/www/html/.well-known/acme-challenge/;
    allow all;
    auth_basic off;
}

# Private Prefetch Proxy (Chrome)
location /.well-known/traffic-advice {
    types { } default_type "application/trafficadvice+json; charset=utf-8";
    return 200 "[{\n  \"user_agent\": \"prefetch-proxy\",\n  \"google_prefetch_proxy_eap\": {\n    \"fraction\": 1.0\n  }\n}]";
    allow all;
}

# Tolak akses file umum dari Git repo atau file sensitif lainnya
location ~* "/(readme|license|example|README|LEGALNOTICE|INSTALLATION|CHANGELOG)\.(txt|html|md)" {
    deny all;
}

# Tolak file backup/log/extensions berbahaya
location ~* \.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf|gz|zip|bz2|7z|pem|asc|conf|dump)$ {
    deny all;
}

# Blok pola URL mencurigakan
location ~* "/(=|\$&|_mm|(wp-)?config\.|cgi-|etc/passwd|muieblack)" {
    deny all;
}

# Blok fungsi atau pola eksploitasi
location ~* "(base64_encode)(.*)(\()" { deny all; }
location ~* "(eval\()" { deny all; }
location ~* "(127\.0\.0\.1)" { deny all; }
location ~* "([a-z0-9]{2000})" { deny all; }
location ~* "(javascript\:)(.*)(\;)" { deny all; }
location ~* "(GLOBALS|REQUEST)(=|\[|%)" { deny all; }
location ~* "(<|%3C).*script.*(>|%3)" { deny all; }
location ~* "(boot\.ini|etc/passwd|self/environ)" { deny all; }
location ~* "(thumbs?(_editor|open)?|tim(thumb)?)\.php" { deny all; }
location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { deny all; }
location ~* "(https?|ftp|php):/" { deny all; }
location ~* "(=\\\'|=\\%27|/\\\'/?)\." { deny all; }
location ~* "(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")" { deny all; }
location ~ "(~|`|<|>|:|;|%|\\|\s|\{|\}|\[|\]|\|)" { deny all; }

Konfigurasi Cache (Opsional)

Langkah ini bersifat opsional dan dapat dilewati jika Anda tidak ingin mengaktifkan cache pada Nginx.

1. Buat File Konfigurasi Cache Umum

Buat file cache_common yang berisi pengaturan cache dasar:

nano /etc/nginx/cache_common

Isi file dengan:

fastcgi_cache_key "$scheme$request_method$host$request_uri";
# Optional cache bypass conditions
# fastcgi_no_cache $no_cache $http_pragma $http_authorization $arg_nocache;
# fastcgi_cache_bypass $no_cache $http_pragma $http_authorization $arg_nocache;

fastcgi_no_cache $no_cache $http_authorization;
fastcgi_cache_bypass $no_cache $http_authorization;

fastcgi_cache_valid 5m;
fastcgi_cache_use_stale http_500 http_503 updating;
fastcgi_cache_background_update on;

2. Buat File custom_cache untuk Pengaturan Dinamis

File ini berisi logika tambahan untuk menentukan kapan cache harus dinonaktifkan, misalnya untuk pengguna login atau area admin:

nano /etc/nginx/custom_cache

Isi file dengan:

add_header X-Cache-Status $upstream_cache_status;

set $no_cache "";
set $cache_cookie $http_cookie;

# Nonaktifkan cache jika ada cookie
if ($cache_cookie !~ "^\s*$") {
    set $no_cache 1;
}

# Nonaktifkan cache untuk CMS dan ekstensi yang umum
if ($http_cookie ~* "(joomla_[a-zA-Z0-9_]+|userID|wordpress_(?!test_)[a-zA-Z0-9_]+|wp-postpass|comment_author_[a-zA-Z0-9_]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-zA-Z0-9]+|wordpress_logged_in_[a-zA-Z0-9]+|sid_customer_|sid_admin_|PrestaShop-[a-zA-Z0-9]+|SESS[a-zA-Z0-9]+|SSESS[a-zA-Z0-9]+|NO_CACHE|external_no_cache|adminhtml|private_content_version)") {
    set $no_cache 1;
}

# Nonaktifkan cache untuk area admin CMS
if ($request_uri ~* "(/administrator|/wp-admin|/wp-login.php)") {
    set $no_cache 1;
}

3. Tambahkan Konfigurasi Cache di web1.conf

Edit file virtualhost Anda:

nano /etc/nginx/conf.d/web1.conf

Tambahkan konfigurasi berikut (pastikan fastcgi_cache_path berada di luar blok server {}):

fastcgi_cache_path /var/cache/nginx/web1.example.com levels=1:2 keys_zone=web1.example.com:50m max_size=256m;

server {
    listen 80;
    server_name web1.example.com;

    client_max_body_size 128m;

    access_log  /var/log/nginx/web1.access.log rt_cache;
    error_log   /var/log/nginx/web1.error.log;

    root /home/web1/public_html;
    index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
    disable_symlinks if_not_owner from=/home/web1/public_html;

    # SEO-friendly permalink fallback
    set $sef_entry_point /;
    if ($uri ~* "^/") {
        set $sef_entry_point "/index.php?$args";
    }

    location @wpt_permalinks_fallback {
        try_files $uri $sef_entry_point;
    }

    error_page 404 = @wpt_permalinks_fallback;
    error_page 405 = @wpt_permalinks_fallback;

    include location.conf;
    include custom_cache;

    location ~ \.php(/.*)?$ {
        try_files $uri $fastcgi_script_name =404;
        fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass unix:/var/opt/remi/php74/run/php-fpm/web1.sock;
        fastcgi_read_timeout 300;
        fastcgi_cache web1.example.com;
        include /etc/nginx/fastcgi.conf;
        include /etc/nginx/cache_common;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

Restart Service

Terakhir, restart layanan PHP-FPM dan Nginx agar perubahan konfigurasi diterapkan:

systemctl restart php74-php-fpm nginx

Test akses web melalui http://web1.example.com

Optimasi

Berikut merupakan tambahan konfigurasi untuk optimasi webserver dan php-fpm.

Langkah ini opsional dan dapat Anda lewati.

nginx.conf

worker_processes  auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;

pcre_jit on;

events {
    multi_accept on;
    use epoll;
    worker_connections 65535;
    accept_mutex on;
}

http {
    ## Basic Settings ##
    server_tokens                  off;
    aio                            threads;
    client_body_buffer_size        128k;
    client_header_buffer_size      1k;
    large_client_header_buffers    4 16k;
    client_body_timeout            30s; # Use 5s for high-traffic sites
    client_header_timeout          30s; # Use 5s for high-traffic sites
    client_max_body_size           1024m;
    keepalive_requests             1024;
    keepalive_timeout              30s;
    send_timeout                   30s;
    open_file_cache                max=200000 inactive=20s;
    open_file_cache_errors         on;
    open_file_cache_min_uses       2;
    open_file_cache_valid          30s;
    port_in_redirect               off;
    reset_timedout_connection      on;
    sendfile                       on;
    server_name_in_redirect        off;
    server_names_hash_bucket_size  1024;
    server_names_hash_max_size     1024;
    tcp_nodelay                    on;
    tcp_nopush                     on;
    types_hash_max_size            2048;
    client_body_temp_path 	   /tmp/client_temp;
    proxy_temp_path                /tmp/proxy_temp_path;
    fastcgi_temp_path              /tmp/fastcgi_temp;
    uwsgi_temp_path                /tmp/uwsgi_temp;
    scgi_temp_path                 /tmp/scgi_temp;

    ## Gzip Settings ##
    gzip on;
    gzip_buffers 16 8k;
    gzip_comp_level 6;
    gzip_disable "msie6";
    gzip_min_length 1000;
    gzip_proxied any;
    gzip_types
        application/atom+xml
        application/geo+json
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/rss+xml
        application/vnd.geo+json
        application/vnd.ms-fontobject
        application/wasm
        application/x-font-opentype
        application/x-font-truetype
        application/x-font-ttf
        application/x-javascript
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        application/xml+rss
        font/eot
        font/opentype
        font/otf
        image/bmp
        image/svg+xml
        image/vnd.microsoft.icon
        image/x-icon
        image/x-win-bitmap
        text/cache-manifest
        text/calendar
        text/css
        text/javascript
        text/markdown
        text/plain
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-component
        text/x-cross-domain-policy
        text/x-js
        text/xml;
    gzip_vary on;
    gzip_static on;
    gzip_http_version 1.1;

    ## Security Headers ##
    more_set_headers "X-XSS-Protection: 1; mode=block";
    more_set_headers "X-Frame-Options: SAMEORIGIN";
    more_set_headers "X-Content-Type-Options: nosniff";
    more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";

    ## Log format Settings ##
    log_format rt_cache '$remote_addr $upstream_response_time $upstream_cache_status [$time_local] '
    '$host "$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" "$server_protocol" "$http3"';

    ## SSL Settings ##

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'TLS13+AESGCM+AES256:TLS13+AESGCM+AES128:TLS13+CHACHA20:EECDH+AESGCM:EECDH+CHACHA20';
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519:P-521:P-384:P-256;

    # Enable 0-RTT support for TLS 1.3
    proxy_set_header Early-Data $ssl_early_data;
    ssl_early_data on;

    # enable http/2
    http2 on;

    # oscp settings
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 1.1.1.1 8.8.4.4 1.0.0.1 valid=300s;
    resolver_timeout 10;

    # tls dynamic records patch directive
    ssl_dyn_rec_enable on;

    ## Logging Settings ##
    access_log off;
    error_log /var/log/nginx/error.log;

    ## ngx_vts_module ##
    vhost_traffic_status_zone;

dhparams2048.pem

openssl dhparam -out dhparams2048.pem 2048

conf.d/ssl.conf

ssl_dhparam /etc/nginx/dhparams2048.pem;
proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=15552000; preload" always;

php-fpm.conf

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s

/etc/security/limits.conf

*         hard    nofile      500000
*         soft    nofile      500000
root      hard    nofile      500000
root      soft    nofile      500000

/opt/myscript/kernel.sh

echo 1 >/sys/kernel/mm/ksm/run
echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs
echo never > /sys/kernel/mm/transparent_hugepage/enabled

/etc/modules-load.d/htcp.conf

tcp_htcp

HA

Untuk memastikan skalabilitas, ketersediaan tinggi, dan performa optimal, Anda dapat mengonfigurasi dua PHP-FPM pool untuk satu virtual host. Langkah ini bersifat opsional.

Referensi: