Server Block #
Di era modern pengembangan web, menjalankan satu situs web pada satu server fisik tunggal adalah pemborosan sumber daya komputasi yang besar. Untuk mengatasi hal ini, Nginx menyediakan mekanisme virtual hosting tingkat tinggi melalui konsep Server Block. Server block memungkinkan kita meng-host puluhan, ratusan, bahkan ribuan nama domain yang berbeda secara terisolasi hanya dengan menggunakan satu alamat IP publik dan satu instansi Nginx yang berjalan di server.
Memahami cara kerja server block bukan sekadar menuliskan nama domain di file konfigurasi. Kita harus memahami bagaimana Nginx berinteraksi dengan TCP stack pada kernel Linux, bagaimana ia memilih server block yang tepat berdasarkan header protokol HTTP, serta bagaimana merancang server block yang aman dari serangan pemindaian port (port scanning). Artikel ini akan membahas konsep virtual hosting, membedah parameter optimasi socket pada directive listen, menganalisis prioritas pencocokan domain pada server_name, serta menyusun konfigurasi pertahanan default server.
Konsep Virtual Hosting: Name-Based vs IP-Based #
Di tingkat jaringan komputer, server block diimplementasikan melalui dua model utama virtual hosting:
1. IP-Based Virtual Hosting #
Pada model ini, server fisik kita harus memiliki beberapa kartu jaringan fisik (NIC) atau beberapa IP alias pada satu kartu jaringan. Setiap situs web diikat ke alamat IP yang unik.
- Cara Kerja: Nginx mendengarkan request pada IP yang berbeda. Klien terhubung ke IP spesifik tersebut, dan Nginx menyajikan situs yang sesuai tanpa perlu memeriksa konten header HTTP.
- Kekurangan: Sangat boros alamat IP. Mengingat kelangkaan alamat IPv4 global saat ini, mengalokasikan satu IP publik per domain untuk ratusan situs web adalah hal yang mustahil dilakukan di sebagian besar lingkungan produksi.
2. Name-Based Virtual Hosting #
Model ini adalah standar industri yang dominan saat ini. Semua situs web berbagi satu alamat IP publik yang sama dan didengarkan pada port jaringan yang sama (misalnya port 80 untuk HTTP dan 443 untuk HTTPS).
- Cara Kerja: Nginx membedakan situs yang diminta oleh klien dengan cara memeriksa Header
Hostdi dalam HTTP request payload yang dikirimkan oleh browser. - Contoh: Ketika pengguna mengakses
example.comdanother.comyang menunjuk ke IP server yang sama (103.22.44.55), browser mengirimkan request HTTP dengan header:Host: example.com-> Nginx mengarahkan ke virtual host A.Host: other.com-> Nginx mengarahkan ke virtual host B.
Bedah Mendalam Parameter Directive listen #
Directive listen memberitahu Nginx pada alamat IP, port, atau UNIX path mana worker process harus membuka soket jaringan untuk mendengarkan koneksi masuk. Penulisan directive ini sangat fleksibel dan mendukung parameter tingkat kernel yang sangat kuat:
# 1. Menunggu di semua interface IPv4 pada port 80
listen 80;
# 2. Menunggu di alamat IP spesifik pada port 80
listen 192.168.1.50:80;
# 3. Menunggu di semua interface IPv6
listen [::]:80;
# 4. Menunggu pada UNIX Domain Socket (inter-process communication cepat)
listen unix:/var/run/nginx.sock;
# 5. Konfigurasi HTTPS dengan parameter optimasi tingkat lanjut
listen 443 ssl default_server reuseport backlog=4096;
Mari kita ulas parameter-parameter optimasi low-level di dalam directive listen di atas:
default_server- Menandai server block ini sebagai tujuan default jika request masuk tidak cocok dengan
server_namemanapun pada port tersebut. Hanya boleh ada satu deklarasidefault_serverper kombinasi IP:Port.
- Menandai server block ini sebagai tujuan default jika request masuk tidak cocok dengan
reuseport- Mekanisme Kerja: Secara default, semua worker process berbagi satu antrean soket masuk (listen socket queue) yang dikelola oleh kernel. Saat trafik sangat tinggi, hal ini memicu perebutan kunci (lock contention) antar worker yang meningkatkan latensi CPU. Dengan parameter
reuseport, kernel Linux membuat antrean soket terpisah untuk setiap worker process. Kernel secara otomatis mendistribusikan koneksi TCP masuk ke worker-worker tersebut menggunakan hashing hardware. - Hasil: Menurunkan latensi jabat tangan (handshake) TCP secara drastis pada sistem multicore dan meningkatkan throughput global hingga 3 kali lipat.
- Mekanisme Kerja: Secara default, semua worker process berbagi satu antrean soket masuk (listen socket queue) yang dikelola oleh kernel. Saat trafik sangat tinggi, hal ini memicu perebutan kunci (lock contention) antar worker yang meningkatkan latensi CPU. Dengan parameter
backlog=4096- Menentukan panjang maksimum antrean koneksi TCP yang sedang menunggu jabat tangan selesai (half-open TCP connection queue atau SYN backlog). Jika antrean melebihi nilai ini, kernel Linux akan menolak koneksi baru. Nilai bawaan sistem operasi biasanya hanya
511. Menaikkannya menjadi4096sangat disarankan untuk server bertrafik padat agar tidak menolak klien saat terjadi lonjakan trafik tiba-tiba.
- Menentukan panjang maksimum antrean koneksi TCP yang sedang menunggu jabat tangan selesai (half-open TCP connection queue atau SYN backlog). Jika antrean melebihi nilai ini, kernel Linux akan menolak koneksi baru. Nilai bawaan sistem operasi biasanya hanya
ssl- Menginstruksikan Nginx bahwa koneksi yang masuk ke port ini wajib melalui proses negosiasi enkripsi SSL/TLS terlebih dahulu sebelum memproses protokol HTTP.
Manajemen Dual-Stack IPv4 dan IPv6 #
Saat mengaktifkan IPv6 di server, kita sering kali menghadapi masalah di mana Nginx gagal melakukan bind soket IPv4 karena kernel sistem operasi telah mendengarkan port tersebut via IPv6 secara bertumpuk (IPv6-only binding behavior).
Untuk menghindari tabrakan port di Linux, gunakan parameter ipv6only=on jika ingin memisahkannya, atau cukup pasang konfigurasi berikut agar Nginx mendengarkan kedua protokol secara dual-stack yang bersih:
server {
# Dengarkan IPv4 di semua interface
listen 80;
# Dengarkan IPv6 di semua interface secara independen
listen [::]:80 ipv6only=on;
server_name example.com;
}
Membongkar Sintaksis server_name: Jenis & Prioritas #
Setelah Nginx menerima koneksi TCP pada port yang ditentukan, ia memasuki tahap kedua: mencocokkan header Host dari HTTP request dengan directive server_name yang terdefinisi di dalam server block kita.
Nginx mendukung empat metode penulisan domain di dalam server_name:
1. Exact Match (Nama Domain Tepat) #
Pencocokan secara harfiah dengan domain lengkap. Ini adalah cara yang paling umum digunakan.
server_name example.com www.example.com;
2. Wildcard Match (Nama dengan Asterisk *)
#
Nginx mengizinkan penggunaan tanda bintang * untuk mencocokkan bagian subdomain. Tanda bintang hanya boleh diletakkan di awal atau di akhir domain, dan hanya mencakup satu segmen nama:
# Mencocokkan: api.example.com, blog.example.com
# Tidak mencocokkan: example.com, atau dev.api.example.com (dua tingkat)
server_name *.example.com;
# Mencocokkan: example.org, example.net (suffix wildcard)
server_name example.*;
3. Regular Expression Match (Pencocokan Regex) #
Jika kita membutuhkan logika pencocokan yang kompleks, kita dapat menggunakan regex dengan mengawali nilai server_name menggunakan tanda tilde ~.
# Mencocokkan domain dengan opsional www. dan sub-domain internal
server_name ~^(www\.)?example\.com$;
Kita juga dapat menggunakan fitur Capture Groups untuk menangkap bagian domain secara dinamis dan menyimpannya ke dalam variabel kustom. Variabel ini kemudian dapat kita gunakan di dalam konfigurasi server block, misalnya untuk menentukan folder root secara otomatis:
# Menangkap nama subdomain ke dalam variabel $subdomain
server_name ~^(?<subdomain>.+)\.example\.com$;
# Menggunakan variabel yang ditangkap untuk menyusun path root dinamis
root /var/www/html/subdomains/$subdomain;
Dengan taktik di atas, jika klien mengakses customer-a.example.com, Nginx secara dinamis mengarahkan root direktori ke /var/www/html/subdomains/customer-a tanpa kita perlu menulis puluhan server block manual untuk setiap pelanggan baru.
4. Blank Name / Catch-All #
server_name _;
Karakter underscore _ tidak memiliki makna magis; ia hanyalah nama domain tidak valid yang digunakan sebagai konvensi global untuk menandai “catch-all” (server block yang mencocokkan semua request yang tidak cocok dengan nama server riil manapun).
Algoritma Urutan Prioritas Evaluasi server_name #
Jika terdapat banyak server block yang mendengarkan pada port yang sama dan beberapa di antaranya berpotensi cocok dengan domain request (misalnya exact match vs regex), Nginx mengevaluasi kecocokan tersebut berdasarkan urutan prioritas yang sangat ketat:
1. Exact Name Match (paling diprioritaskan)
-> server_name example.com;
2. Wildcard Match terpanjang yang diawali tanda bintang (*)
-> server_name *.example.com;
3. Wildcard Match terpanjang yang diakhiri tanda bintang (*)
-> server_name example.*;
4. Regular Expression (Regex) Match, dievaluasi dalam URUTAN kemunculannya di file konfigurasi
-> server_name ~^(www\.)?example\.com$;
5. default_server (jika tidak ada pencocokan dari langkah 1-4 yang berhasil)
Sebagai ilustrasi, jika kita memiliki konfigurasi berikut:
# Server Block 1
server {
listen 80;
server_name *.example.com;
}
# Server Block 2
server {
listen 80;
server_name ~^api\.example\.com$;
}
Ketika ada request untuk api.example.com, Nginx akan memilih Server Block 1 karena pencocokan wildcard awal (Prioritas 2) memiliki derajat yang lebih tinggi daripada pencocokan regex (Prioritas 4), meskipun ekspresi regex pada Server Block 2 sangat tepat mendefinisikan domain tersebut.
Limitasi dan Penyetelan Server Name Hash Tables #
Saat kita mengelola infrastruktur web server berskala besar dengan ratusan nama domain virtual host, atau ketika kita menggunakan nama domain yang sangat panjang (seperti subdomain bertingkat untuk sistem multi-tenant), Nginx sering kali gagal melakukan inisialisasi memori saat dinyalakan dan memuntahkan error fatal berikut:
nginx: [emerg] could not build optimal server_names_hash, you should increase either server_names_hash_max_size: 512 or server_names_hash_bucket_size: 64
Mengapa Masalah Ini Terjadi? #
Untuk melakukan pencocokan nama server secepat kilat pada memori RAM, Nginx tidak memindai daftar domain kita secara sekuensial dari atas ke bawah untuk setiap request. Sebaliknya, Nginx membangun tabel hash (Hash Tables) dari seluruh nama server pada saat startup.
Ukuran memori yang dialokasikan untuk setiap entri domain ditentukan oleh dua parameter:
server_names_hash_max_size: Batas ukuran maksimum tabel hash secara keseluruhan.server_names_hash_bucket_size: Ukuran maksimum satu ember (bucket) memori yang dialokasikan untuk menyimpan satu nama domain (termasuk karakter string domain tersebut). Secara default, Nginx menyetel ukuran ini selaras dengan ukuran cache line CPU kita (biasanya 32, 64, atau 128 bytes).
Jika salah satu domain kita sangat panjang, nama domain tersebut tidak akan muat dalam ruang bucket memori default. Akibatnya, Nginx gagal merakit tabel pencarian biner dan langsung menolak berjalan.
Solusi Praktis Penyetelan Tabel Hash #
Kita wajib menaikkan kapasitas tabel hash ini di tingkat global http context pada file nginx.conf:
http {
# Tambahkan atau ubah parameter berikut di http context
# Naikkan ukuran bucket ke 128 byte (mendukung nama domain yang panjang)
server_names_hash_bucket_size 128;
# Naikkan batas maksimum ukuran tabel hash global
server_names_hash_max_size 1024;
# ... konfigurasi lainnya ...
}
Menyetel server_names_hash_bucket_size menjadi 128 adalah pilihan aman yang direkomendasikan pada arsitektur server produksi modern, memberikan ruang yang cukup bagi nama subdomain dinamis tanpa memicu peningkatan konsumsi RAM yang berarti.
Keamanan Default Server: Mengapa Wajib & Cara Setup #
Secara default, jika Nginx menerima request dengan header Host yang tidak terdaftar di server block manapun, Nginx akan melimpahkan request tersebut ke server block pertama yang dimuat pada port bersangkutan.
Ancaman Keamanan Pemindaian Massal (Port Scanning) #
Bot peretas dan pemindai internet secara berkala menelusuri seluruh rentang alamat IP publik untuk mencari port 80 dan 443 yang terbuka. Mereka mengirimkan request HTTP mentah langsung ke IP server kita (misalnya GET / HTTP/1.1 dengan header Host: 103.22.44.55).
Jika kita tidak mengonfigurasi default server khusus, Nginx secara otomatis akan merespons menggunakan virtual host pertama kita. Hal ini berbahaya karena:
- Eksposur Domain: Membocorkan domain dan teknologi internal yang berjalan di server kita ke pihak luar.
- Serangan Eksploitasi: Bot akan mencoba mengirimkan eksploitasi web aplikasi ke endpoint kita langsung melalui IP.
- Pemborosan Resources: Server kita sibuk melayani trafik sampah yang tidak berguna.
Solusi: Penerapan Default Server Pemutus Koneksi #
Praktik keamanan terbaik industri mewajibkan kita membuat satu server block default khusus di port HTTP (80) dan HTTPS (443) yang bertindak sebagai “dinding pertahanan”. Blok ini bertugas menangkap seluruh request sampah tersebut dan langsung memutus koneksinya tanpa memberikan informasi apapun.
# /etc/nginx/conf.d/00-default-security.conf
# 1. Menolak trafik port 80 yang tidak valid
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Kembalikan status kustom Nginx 444
return 444;
}
# 2. Menolak trafik port 443 yang tidak valid
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
# Karena SSL listen butuh sertifikat valid untuk handshake awal,
# kita pasang sertifikat kosong/self-signed dummy khusus untuk pertahanan ini
ssl_certificate /etc/nginx/ssl/dummy.crt;
ssl_certificate_key /etc/nginx/ssl/dummy.key;
return 444;
}
Mengapa Menggunakan Kode 444?
#
Status 444 Connection Closed Without Response adalah status HTTP internal non-standar khusus milik Nginx. Ketika Nginx mengeksekusi return 444;, Nginx akan langsung menutup koneksi TCP soket klien seketika itu juga tanpa menulis header HTTP respons (seperti Server: nginx atau status 400 Bad Request) dan tanpa mengirimkan byte data satu pun. Klien pemindai hanya akan melihat error koneksi kosong (Empty Response). Ini sangat efektif untuk menghemat memori, bandwidth, dan CPU server kita.
Skenario Pengalihan Trafik (HTTP to HTTPS Redirection) #
Dalam standar web modern, seluruh trafik web wajib berjalan di atas protokol terenkripsi HTTPS. Untuk mencapainya, kita harus merancang pola pengalihan (redirection) trafik HTTP (port 80) ke HTTPS (port 443) yang efisien dan aman.
Berikut adalah pola konfigurasi redirect HTTP ke HTTPS standar industri:
# /etc/nginx/conf.d/example.com.conf
# 1. Server Block HTTP (Port 80) — Melakukan Redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Redirect permanen 301 ke HTTPS
return 301 https://$host$request_uri;
}
# 2. Server Block HTTPS (Port 443) — Menangani Request Riil
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
# Lokasi sertifikat SSL Let's Encrypt
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Analisis Parameter Redirect: #
301: Status kode HTTP untuk Moved Permanently. Status ini sangat penting untuk SEO karena memberi tahu mesin pencari (seperti Google) bahwa alamat resmi situs kita telah berpindah ke HTTPS secara permanen, sehingga otoritas peringkat (page rank) dipindahkan ke URL HTTPS.$host: Variabel bawaan Nginx yang berisi nama domain yang diminta oleh klien (dari headerHost).$request_uri: Variabel bawaan yang berisi path URL lengkap beserta parameter query string-nya (misal/blog/tutorial-nginx?page=2).- Hasil: Jika klien mengakses
http://www.example.com/blog/tutorial-nginx?page=2, Nginx secara instan mengarahkannya kehttps://www.example.com/blog/tutorial-nginx?page=2dengan aman dan tanpa memotong parameter pencarian aplikasi.
Diagram Alur Seleksi Server Block Nginx #
Untuk meringkas pemahaman kita, diagram berikut menjelaskan bagaimana Nginx memproses request dari awal koneksi hingga server block terpilih:
flowchart TD
Init["Paket Jaringan Tiba di Port Jaringan"] --> Step1["1. Cari kecocokan IP & Port pada directive 'listen'"]
Step1 -->|"Tidak Ada Port Cocok"| Err1["Abaikan / Tolak Koneksi"]
Step1 -->|"Ada Beberapa Server Block"| Step2["2. Periksa Header 'Host' pada Request HTTP"]
Step2 -->|"Cocok Persis (Exact)"| MatchExact["Gunakan Server Block (Exact Match)"]
Step2 -->|"Tidak Cocok Exact"| Step3["3. Evaluasi Wildcard Awal (*.domain)"]
Step3 -->|"Cocok Wildcard Awal"| MatchWA["Gunakan Server Block (Wildcard Awal)"]
Step3 -->|"Tidak Cocok Wildcard Awal"| Step4["4. Evaluasi Wildcard Akhir (domain.*)"]
Step4 -->|"Cocok Wildcard Akhir"| MatchWZ["Gunakan Server Block (Wildcard Akhir)"]
Step4 -->|"Tidak Cocok Wildcard Akhir"| Step5["5. Evaluasi Regular Expression (Regex)"]
Step5 -->|"Cocok Regex"| MatchRegex["Gunakan Server Block Regex <br> (Sesuai urutan deklarasi file)"]
Step5 -->|"Tidak Cocok Regex"| Step6["6. Apakah ada block dengan 'default_server' <br> pada IP:Port tersebut?"]
Step6 -->|"Ada default_server"| MatchDefault["Gunakan Server Block default_server"]
Step6 -->|"Tidak Ada default_server"| MatchFirst["Fallback: Gunakan Server Block Pertama <br> yang di-load secara alfabetis"]
style Init stroke:#0288d1,stroke-width:2.5px
style Step2 stroke:#f57c00,stroke-width:2px
style MatchExact stroke:#388e3c,stroke-width:1.5px
style MatchDefault stroke:#d32f2f,stroke-width:2pxRingkasan #
- Name-Based Virtual Hosting memanfaatkan HTTP Header
Hostuntuk membedakan puluhan situs web yang berjalan di satu alamat IP dan port yang sama.- Directive
listenmengontrol pengikatan soket. Gunakan parameterreuseportpada server multicore bertrafik tinggi untuk mengurangi CPU lock contention, serta tingkatkanbackloguntuk menampung lonjakan TCP queue.- Urutan prioritas
server_namedievaluasi ketat: exact match → wildcard awal → wildcard akhir → regex (urutan pembacaan file) → default server fallback.- Tangkap variable regex subdomain dengan syntax
(?<name>pattern)padaserver_nameuntuk membangun penanganan direktori root dinamis secara otomatis.- Naikkan
server_names_hash_bucket_sizemenjadi128dihttpblock jika kita mengelola banyak sub-domain atau nama domain panjang guna menghindari kegagalan startup memori.- Proteksi server kita dari serangan port scanner dengan memasang server block
default_serverkhusus yang mengembalikan status444untuk menolak request IP mentah secara instan tanpa respons data.
← Sebelumnya: Directive & Context Berikutnya: Location Block →