Skip to content

nginx optimization

Muhamad Hanafiah Yahya edited this page Sep 29, 2022 · 4 revisions

Generator https://www.digitalocean.com/community/tools/nginx

Old stype PHP

default.d/php.conf

index index.php index.html index.htm;
location ~ \.php${
    try_files $uri = 404;
    fastcgi_intercept_errors on;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass php-fpm;
}

conf.d/php-fpm.conf

upstream php-fpm{
    server unix:/run/php-fpm/www.sock;
}

Modern PHP

conf.d/vhost.conf

server{
    listen 80;
    server_name localhost;

    root /var/www/html;

    location / {
        try_files $uri @php;
    }

    location @php {
        fastcgi_pass     php-fpm;
        include          fastcgi_params;
        fastcgi_param    REQUEST_URI $document_uri;
        fastcgi_param    SCRIPT_FILENAME /var/www/php/index.php;         
    }
}

usual PHP

    location / {
        root /var/www/html;
        index index.html index.htm;
    }

    location ~ \.php$ {
        root html;
        fastcgi_pass     127.0.0.1:9000;
        fastcgi_index    index.php
        include          fastcgi_params;
        fastcgi_param    SCRIPT_FILENAME /var/www/php/index.php;         
    }
}

simplify

    root /var/www/html;
    location / {
        try_files $uri $uri/ @php;
    }

    location @php {
        fastcgi_pass     127.0.0.1:9000;
        include          fastcgi_params;
        fastcgi_param    SCRIPT_FILENAME /var/www/php/index.php;         
    }
}

Load Balancing

conf.d/php-pool.conf

upstream php-pool{
    server unix:/run/php-fpm/www.sock max_cons=10;

    server 192.168.1.1:9000 weight=5; # pemberat untuk round robin
    server 192.168.1.2:1345 max_fails=3 fails_timeout=30s; # letak max failed dan timeout
    server 192.168.1.3:9000 down; # tag server sbg down

    server 192.168.1.4:9000 backup; # backup akan ignore by round robin
}

Keep Alive

conf.d/php-pool.conf

upstream php-pool {
      server 192.168.0.1:9000;
      server 192.168.0.2:9000;

     keepalive 10;
}

conf.d/vhost-pool.conf

server {
    # ...
    location @php{
        fastcgi_pass php-pool;
        fastcgi_keep_conn on;
    }
}

GZip

conf.d/gzip-vhost.conf

server{
    listen 80;
    server_name localhost;

    root /var/www/html;

    location / {
        gzip_static on;
        try_files $uri @php;
    }

    location @php {
        gzip on;
        gzip_types applicaion/xhtml+xml;

        # ...         
    }
}

Cache static files

conf.d/static.conf

location ~* \.(?:ico|gif|png|svg|jp?g|eot|tff|woff|woff2)$ {
    expires 7d;
}

Build-in Tracking

conf.d/tracking.conf

location = / .gif {
    empty_gif;
    access_log /var/log/tracking_log;
}

Language based routing

conf.d/map.conf

map $http_accept_language $lang {
    default en;
    ~ms ms;
    # ...
}

server {
    location = / {
        index index.$lang.html;
    }
}

A/B Testing

conf.d/abtesting.conf

split_clients "${remote_addr}AAA" $variant {
   0.5% A;
   2.0% B;
   *    C;
}

server{
    location @php {
        # ..
        fastcgi_param SCRIPT_FILENAME /var/www/php/${variant}/index.php
    }
}

alternative

server{
    location @php {
        # ..
        fastcgi_param X-variant $variant;
        fastcgi_param SCRIPT_FILENAME /var/www/php/index.php # handle variab dalam php gunakan server variable X-variant
    }
}

Large File Uploads

conf.d/upload.conf

server{
    # ..

    location /upload {
        limit_except POST { deny all; }
        
        client_body_temp_path /var/www/data/uploads;
        client_body_in_file_only on;
        client_body_buffer_size 1024k;
        client_max_body_size 10G;

        # ...
        fastcgi_pass_request_headers on;
        fastcgi_pass_request_body off;
        fastcgi_ignore_client_abort on;
        fastcgi_param X-UPLOAD-FILE $request_body_file;

        # ...
  
    }
}

/var/www/html/upload.js

let file = formElement.querySelector('input[type=file]').files[0];
let xhr = new XMLHttpRequest();

xhr.open('POST', '/upload', true);
xhr.setRequestHeader('X-Original', file.name);
xhr.send(file);

Download Attachement

conf.d/attachment.conf

location ~ /download/ {
    if( $request_filename ~ "^.*/(.+\.jpg)$"){
        set $fname $1;
        add_header Content-Disposition 'attachment; filename="$fname"';
    }
}

Download with Auth

conf.d/auth-download.conf

location = / {
    try_files $uri @php;
}

location @php {
    # ...
}

location @download {
    if( $request_filename ~ "^.*/(.+\.jpg)$"){
        set $fname $1;
        add_header Content-Disposition 'attachment; filename="$fname"';
    }
}

DownloadQuery.php

class DownloadQuery {
    public function execute(): Response {
        if( $this->acl->isAccessGranted( $this->file ) {
            return DownloadResponse( $this->base . '/' . $file);
        }
        return AccessDeniedResponse();
    }
}

DownloadResponse.php

class DownloadResponse implements Response {
    public function send() {
        header('X-Accel-Redirect: '. $this->file );
    }
}

API Rate limits

conf.d/ratelimit.conf

limit_req_zone $binary_remote_addr zone=applimit:10m rate=10r/s;

location /api/ {
    limit_req zone=apilimit burst=20 nodelay;
    limit_req_status 403;
    # ...
}

alternative conf.d/auth-ratelimit.conf

server {
    location /api/ {
        # ...
        fastcgi_param SCRIPT_FILENAME /var/www/api/gateway.php;

    }

    location @api {
        add_header X-Ratelimit-Limit $upstream_http_x_ratelimit_limit;
        add_header X-Ratelimit-Remaining $upstream_http_x_ratelimit_remaining;
        add_header X-Ratelimit-Reset $upstream_http_x_ratelimit_reset;
    }
}

gateway.php

<?php

$limit = new LimitClient( new RedisClient(), new TokenStore());
$usage = $limit->getUsageFor($_SERVER['HTTP_AUTHORIZATION'] ?: $SERVER['REMOTE_ADDR']);

if($usage->isWithinLimit()){
    header('X-Accel-Redirect: @api');
}else{
    http_response_code(403);
}

header('X-RateLimit-Limit: '. $usage->getTotalLimit());
header('X-RateLimit-Remaining: '. $usage->getRemaining());
header('X-RateLimit-Reset: '. $usage->getResetTime()->getTimestamp());

die();

Ring Down Chain

conf.d/chain.conf

server {
    location @php {
        # ...
        fastcgi_intercept_errors on;
        recursive_error_pages on;
        
        error_page 404 = @NextInLine;
    }

    location @NextInLine{
        # ...
    }

}