SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-13

HTTP Itu Apa? Panduan untuk Admin Server

Pahami HTTP dari sisi admin server: method, status code, header penting, isi log nginx, serta peran TLS dan HTTP/3 dalam setiap request.

HTTP itu apa?

HTTP (hypertext transfer protocol) adalah kumpulan aturan yang digunakan client dan web server untuk meminta sesuatu dan mengirimkan kembali hasilnya. Client mengirim request yang berisi method seperti GET, path seperti /pricing, versi protokol, daftar header, dan terkadang body. Server menjawab dengan status code seperti 200, kemudian header miliknya sendiri dan biasanya body. Setiap tampilan halaman dan setiap pemanggilan API (application programming interface) pada server Anda merupakan satu pertukaran tersebut yang diulang.

HTTP tidak menyimpan state secara bawaan. Server tidak mengingat permintaan yang Anda kirim satu detik sebelumnya. Karena itu, informasi yang berfungsi seperti memori, misalnya login session, dibawa dalam header pada setiap request. Sifat ini menjelaskan banyak hal yang dibahas berikutnya: caching sepenuhnya dikendalikan oleh header, dan load balancer dapat mengirim request berikutnya ke backend yang berbeda tanpa menimbulkan masalah.

Semua bagian di bawah ini menunjukkan bagaimana model tersebut terlihat dari sisi server, dalam access log dan konfigurasi nginx Anda.

Permintaan dan respons mentah, dengan anotasi

Berikut adalah permintaan HTTP/1.1 lengkap. Baris kosong mengakhiri header, dan semua hal setelah baris tersebut adalah body. GET biasanya tidak memiliki body.

GET /pricing HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzip
  • GET adalah method yang menyatakan tindakan yang ingin dilakukan. GET membaca, POST mengirim data, PUT mengganti, DELETE menghapus, dan HEAD meminta header dari GET tanpa body.
  • /pricing adalah path. Hostname bukan bagian dari baris permintaan. Karena itu, header berikutnya diperlukan.
  • HTTP/1.1 adalah versi protokol yang digunakan client.
  • Host: example.com menyebutkan site yang diinginkan client. HTTP/1.1 mewajibkannya, sehingga nginx menjawab permintaan tanpa header tersebut dengan 400 Bad Request.
  • Header lainnya berisi preferensi. Accept-Encoding: gzip menyatakan bahwa client dapat melakukan dekompresi, sehingga server boleh mengompresi body.

Respons memiliki struktur yang sama, dengan baris status di bagian atas.

HTTP/1.1 200 OK
Date: Thu, 06 Aug 2026 09:12:44 GMT
Server: nginx
Content-Type: text/html; charset=utf-8
Content-Length: 5310
Cache-Control: public, max-age=300

<!doctype html>...
  • 200 OK adalah kode status beserta frasa alasannya. Kode tersebut yang penting. Frasa hanya pelengkap, dan client mengabaikannya.
  • Content-Type memberi tahu client cara menangani byte yang mengikutinya.
  • Content-Length adalah ukuran body dalam byte, sehingga client mengetahui posisi akhir body. Jika ukurannya belum diketahui, server mengirim Transfer-Encoding: chunked dan menandai akhir body dengan chunk berukuran nol.
  • Cache-Control memberi tahu browser dan cache di antaranya berapa lama respons ini boleh disimpan.
  • Baris kosong setelah header memisahkan header dari body pada kedua arah.

Nama header tidak membedakan huruf besar dan kecil. Setiap baris diakhiri carriage return yang diikuti line feed, bukan newline biasa. Anda tidak akan mengetiknya secara manual, tetapi akan menemukannya dalam packet capture.

Untuk mengamati pasangan yang nyata, jalankan perintah berikut terhadap site yang Anda miliki:

curl -sS -o /dev/null -D - https://example.com/

-D - menulis header respons ke terminal, sedangkan -o /dev/null membuang body. Gunakan ini daripada curl -I, karena -I mengirim permintaan HEAD. Application server yang menangani HEAD secara berbeda dari GET—dan banyak yang melakukannya—akan menampilkan header yang tidak pernah diterima browser. curl -v mencetak kedua sisi, dengan baris permintaan yang ditandai > dan baris respons yang ditandai <.

Bentuk baris request dalam access log nginx

nginx menyediakan format log combined, dan definisinya adalah:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

Satu baris yang dihasilkan:

203.0.113.45 - - [06/Aug/2026:09:12:44 +0000] "GET /pricing HTTP/1.1" 200 5310 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) Chrome/127.0.0.0 Safari/537.36"
  • 203.0.113.45 adalah $remote_addr, yaitu alamat yang membuka koneksi TCP (transmission control protocol). Di balik proxy, alamat ini adalah proxy, bukan pengunjung.
  • - pertama adalah placeholder tetap. Yang kedua adalah $remote_user, yang hanya diisi jika HTTP basic authentication digunakan.
  • "GET /pricing HTTP/1.1" adalah $request, yaitu baris request yang disalin persis seperti saat diterima.
  • 200 adalah status yang dikembalikan server Anda, bukan status yang dilihat pengunjung.
  • 5310 adalah $body_bytes_sent, yaitu body saja. Header respons tidak dihitung, sehingga angka ini selalu lebih kecil daripada jumlah byte yang sebenarnya dikirim.
  • Dua field terakhir yang diapit tanda kutip adalah Referer dan User-Agent. Keduanya berasal dari client, sehingga keduanya dapat berisi apa saja.

Karena $request disalin secara verbatim, data yang tidak valid juga muncul secara verbatim. Client yang berbicara menggunakan TLS (transport layer security) ke port plaintext 80 Anda meninggalkan baris 400 yang field request-nya diawali byte ter-escape seperti "\x16\x03\x01\x02\x00\x01". \x16 adalah tipe record handshake TLS, sehingga byte tersebut merupakan awal dari ClientHello, bukan baris request sama sekali. Server Anda berfungsi dengan benar. Ada sesuatu yang mengarahkan HTTPS ke port HTTP.

Tambahkan $server_protocol ke format log Anda. Parameter ini mencetak HTTP/1.1, HTTP/2.0, atau HTTP/3.0, dan merupakan cara tercepat untuk membuktikan bahwa perubahan protokol benar-benar diterapkan.

Arti kode status umum saat situs Anda sendiri mengembalikannya

Digit pertama menunjukkan kelas, dan kelas itulah yang harus Anda baca terlebih dahulu.

2xx berarti permintaan berhasil. 200 OK untuk pembacaan biasa. 201 Created setelah POST yang membuat sesuatu. 204 No Content untuk keberhasilan tanpa apa pun yang perlu dikirim kembali, yang merupakan jawaban umum untuk DELETE.

3xx berarti lihat di tempat lain. 301 bersifat permanen dan disimpan kuat oleh browser, terkadang sampai pengguna menghapus profilnya. Karena itu, 301 yang mengarah ke hostname yang salah akan sulit dibatalkan. Gunakan 302 selama Anda masih menguji pengalihan. 304 Not Modified menandakan keberhasilan, bukan error: klien mengirim If-None-Match yang membawa ETag (entity tag) yang masih Anda kenali, sehingga Anda membalas dengan header tanpa body. Log yang dipenuhi 304s berarti caching berfungsi.

4xx berarti request-nya salah. 400 Bad Request menunjukkan input yang formatnya tidak valid. 401 Unauthorized sebenarnya berarti belum diautentikasi dan harus membawa header WWW-Authenticate yang menyebutkan skemanya. 403 Forbidden berarti request dipahami, tetapi tetap ditolak. 404 Not Found adalah path yang tidak ada. 405 Method Not Allowed adalah path yang benar dengan method yang salah, seperti yang dikembalikan oleh POST ke lokasi file statis. 413 berarti body lebih besar daripada client_max_body_size milik nginx, yang secara default bernilai 1 megabyte. Error log mengonfirmasinya dengan client intended to send too large body.

403 pada file statis hampir selalu disebabkan oleh filesystem, bukan aturan HTTP. Baca /var/log/nginx/error.log sebelum mengubah konfigurasi apa pun. open() "/srv/site/index.html" failed (13: Permission denied) berarti user worker nginx tidak dapat membaca file tersebut. Penyebab yang paling sering adalah direktori induk tidak memiliki izin execute untuk user lain. directory index of "/srv/site/" is forbidden berarti path mengarah ke direktori yang tidak memiliki file index saat autoindex dinonaktifkan.

5xx berarti terjadi masalah di sisi Anda. 500 adalah error yang tidak ditangani dalam aplikasi Anda. 502 Bad Gateway berarti nginx tidak dapat memperoleh respons yang dapat digunakan dari upstream. Error log menyebutkan penyebabnya: connect() failed (111: Connection refused) while connecting to upstream berarti tidak ada yang listening pada alamat di proxy_pass. 504 Gateway Timeout berarti upstream menerima koneksi lalu tidak mengirim apa pun dalam waktu proxy_read_timeout, yang secara default adalah 60 detik. Log mencatatnya sebagai upstream timed out (110: Connection timed out) while reading response header from upstream. 503 Service Unavailable adalah penolakan yang disengaja. Perhatikan bahwa rate limiter nginx sendiri mengembalikan 503 karena limit_req_status secara default bernilai 503. Jika Anda mencari 429 Too Many Requests di log tetapi menemukan 503, itulah penyebabnya. Atur limit_req_status 429; untuk memperoleh kode yang tepat.

Header yang penting saat Anda menjalankan server

Host menentukan situs yang dipilih. Satu alamat IP dapat melayani ratusan hostname, dan nginx mencocokkan Host dengan server_name untuk menentukan blok server yang memberikan respons. Jika tidak ada yang cocok, nginx menggunakan server default, yaitu blok pertama yang mendengarkan pada alamat dan port tersebut, kecuali blok lain ditandai sebagai default_server. Jika virtual host baru mengembalikan situs yang salah, penyebabnya hampir selalu ini: namanya tidak cocok, sehingga request diteruskan ke default. Uji tanpa mengubah DNS:

curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/

User-Agent adalah deskripsi diri yang ditulis oleh client dan berupa teks bebas. Gunakan sebagai petunjuk saat membaca log. Jangan pernah menggunakannya sebagai kontrol, karena client yang ingin memalsukannya dapat melakukannya. Jadi, memblokir scraper berdasarkan User-Agent hanya akan menyaring scraper yang tidak menyamarkan diri.

Content-Type menentukan cara byte diinterpretasikan: application/json untuk request API, text/html; charset=utf-8 untuk halaman. nginx memetakan ekstensi file ke tipe menggunakan /etc/nginx/mime.types, dan nginx.conf yang disertakan dalam paket menetapkan default_type application/octet-stream;. Jadi, file dengan ekstensi yang tidak dikenali nginx akan ditawarkan sebagai download, bukan dirender. Gejalanya adalah halaman terbuka tanpa styling, sementara console browser menampilkan Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type. MIME adalah singkatan dari multipurpose internet mail extensions, yaitu skema penamaan asal string tipe tersebut.

Cache-Control digunakan untuk mengontrol setiap cache antara server Anda dan pembaca. public, max-age=31536000, immutable sesuai untuk aset yang nama filenya berisi content hash, karena nama tersebut berubah saat kontennya berubah. no-store harus digunakan untuk apa pun yang spesifik bagi pengguna, karena shared cache yang menyimpan halaman setelah login akan memberikannya kepada orang berikutnya yang meminta URL yang sama. private adalah pengaturan tengah: browser boleh menyimpannya, tetapi shared cache tidak boleh.

X-Forwarded-For diperlukan karena proxy menyembunyikan alamat pengunjung. Setelah request melewati reverse proxy, $remote_addr adalah alamat proxy. Akibatnya, log, geolokasi, dan rate limiting Anda akan melihat satu client yang sama. Proxy harus meneruskan alamat asli:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Server penerima kemudian harus dikonfigurasi agar mempercayai alamat tersebut, dan harus diberi tahu secara tepat alamat siapa yang boleh dipercaya:

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Cantumkan hanya range yang Anda kontrol. X-Forwarded-For adalah teks biasa yang dapat dikirim oleh client mana pun, sehingga set_real_ip_from 0.0.0.0/0; memungkinkan pengunjung menentukan alamat yang dicatat dan alamat yang dihitung oleh rate limiter Anda.

X-Forwarded-Proto mencegah kegagalan yang spesifik dan sangat umum. Proxy Anda melakukan terminasi TLS, lalu meneruskan request ke aplikasi melalui HTTP biasa. Aplikasi melihat request biasa, memutuskan bahwa pengunjung harus menggunakan HTTPS, lalu memberikan respons 301 https://example.com/. Browser mengikutinya, proxy kembali melakukan terminasi TLS dan meneruskan HTTP biasa, lalu siklus tersebut berulang sampai browser menyerah dengan ERR_TOO_MANY_REDIRECTS. Mengirim X-Forwarded-Proto: https memberi tahu aplikasi bahwa pengunjung sudah menggunakan HTTPS, sehingga aplikasi berhenti mengarahkan ulang.

HTTP/1.1 vs HTTP/2 vs HTTP/3: apa yang berubah bagi Anda

HTTP/1.1 berbasis teks dan menangani satu request pada satu waktu untuk setiap koneksi. Connection: keep-alive memungkinkan request berikutnya menggunakan kembali koneksi TCP yang sama sehingga biaya penyiapan berkurang, tetapi respons tetap diterima sesuai urutan request. Satu respons yang lambat akan memblokir semua request yang mengantre di belakangnya. Ini disebut head-of-line blocking. Browser mengatasinya dengan membuka beberapa koneksi ke hostname yang sama secara bersamaan.

HTTP/2 tetap menggunakan method dan status code yang sama, tetapi mengubah framing menjadi biner. Banyak request berbagi satu koneksi sebagai stream yang independen. Teks header yang berulang juga dikompresi. Hal ini penting karena request modern membawa banyak informasi header. Koneksi tersebut tetap menggunakan TCP. Karena itu, satu packet yang hilang akan menghentikan semua stream pada koneksi tersebut sampai retransmission tiba. Head-of-line blocking tidak hilang. Masalah ini berpindah dari HTTP ke lapisan transport. Server push merupakan bagian dari HTTP/2, tetapi pada praktiknya sudah tidak digunakan karena Chrome menghapus dukungannya pada 2022.

HTTP/3 kembali mempertahankan semantik yang sama dan mengganti TCP dengan QUIC, yaitu transport yang dibangun di atas UDP (user datagram protocol). Stream QUIC bersifat independen hingga lapisan terbawah. Karena itu, packet yang hilang hanya menghentikan stream tempat packet tersebut berada. TLS 1.3 terintegrasi dalam handshake QUIC, bukan dilapiskan di atasnya. Dengan demikian, koneksi baru memerlukan lebih sedikit round trip. Ada dua konsekuensi praktis: port UDP 443 harus terbuka pada setiap firewall di sepanjang jalur, dan jaringan yang membatasi atau memblokir UDP akan mengembalikan client ke HTTP/2.

Apa yang berubah bagi Anda secara konkret. Browser tidak pernah memulai koneksi dengan HTTP/3. Browser terhubung melalui HTTP/2 atau HTTP/1.1, menemukan header Alt-Svc: h3=":443"; ma=86400 pada respons, lalu menggunakan HTTP/3 untuk koneksi berikutnya ke host tersebut. Jadi, header ini bukan hiasan opsional. Header ini merupakan mekanisme discovery. Dalam nginx, HTTP/2 menjadi directive tersendiri pada version 1.25.1 (http2 on; di dalam block server, menggantikan parameter listen ... http2 yang lama), dan QUIC hadir di mainline 1.25.0. Pada versi tersebut, site HTTP/3 memerlukan listen 443 quic reuseport; selain listen 443 ssl; biasa.

Tingkat kematangan proxy berbeda-beda dalam hal ini. Anda perlu memeriksanya berdasarkan version yang benar-benar digunakan. Per Agustus 2026, Caddy menyajikan HTTP/3 secara default tanpa konfigurasi. nginx memerlukan listener quic secara eksplisit serta header Alt-Svc yang dijelaskan di atas. Traefik mengaktifkan HTTP/3 per entry point melalui option http3 secara eksplisit. Jika Anda melakukan TLS termination pada Traefik di depan beberapa aplikasi Docker, version protocol yang diterima pengunjung ditentukan di sana. Hop dari proxy ke container Anda biasanya tetap menggunakan HTTP/1.1 biasa, apa pun protocol yang dinegosiasikan browser.

Lakukan verifikasi, bukan asumsi. curl --http3 -sS -o /dev/null -D - https://example.com/ hanya berfungsi jika curl -V mencantumkan HTTP3 di antara fiturnya, dan sebagian besar build distribusi tidak menyertakannya. Pemeriksaan yang dapat diandalkan adalah log Anda sendiri. Tambahkan $server_protocol ke format log, lalu baca protocol yang dinegosiasikan browser sebenarnya. Sebelum melakukan semua itu, pastikan UDP 443 benar-benar terbuka. Firewall yang hanya mengizinkan TCP 443 akan membuat HTTP/3 gagal secara diam-diam, sementara site tetap berfungsi melalui HTTP/2. Mengetahui port mana yang terbuka dan listening pada server Linux Anda adalah hal pertama yang perlu diperiksa.

HTTPS: HTTP adalah protokol, TLS adalah pembungkus

HTTPS bukan protokol terpisah. HTTPS menggunakan request dan status code yang sama, tetapi membawanya di dalam sesi TLS. Port 80 membawanya tanpa enkripsi, sedangkan port 443 membawanya dalam keadaan terenkripsi. TLS handshake selesai terlebih dahulu, kemudian HTTP request dikirim melalui channel terenkripsi. Urutan ini menjelaskan mengapa masalah sertifikat tidak pernah disertai status code: kegagalan terjadi sebelum satu byte HTTP pun dikirim, sehingga tidak ada response yang dapat diberi nomor.

Ada satu detail urutan yang penting pada server yang meng-host beberapa situs. Sertifikat dipilih menggunakan SNI (server name indication), yaitu field dalam TLS handshake yang membawa hostname dalam keadaan terbuka sebelum HTTP header tersedia. Jadi, server memilih sertifikat berdasarkan SNI terlebih dahulu, lalu memilih virtual host berdasarkan header Host. Keduanya adalah dua pencarian terpisah yang biasanya menghasilkan kecocokan. Jika tidak cocok, browser menampilkan ketidakcocokan nama seperti NET::ERR_CERT_COMMON_NAME_INVALID dan sama sekali tidak mengirim request, karena sertifikat dari default server ditawarkan untuk nama yang tidak dicakupnya.

Untuk situs publik, gunakan sertifikat yang valid dan biarkan sertifikat tersebut diperbarui secara otomatis. Certbot dengan Let's Encrypt pada nginx menuliskan path sertifikat ke dalam server block dan memasang timer pembaruan untuk Anda. Untuk hostname yang tidak dapat divalidasi oleh otoritas publik, seperti nama internal atau alamat IP tanpa hostname pada jaringan Anda sendiri, sertifikat self-signed pada Ubuntu adalah pilihan yang tepat, selama Anda menerima bahwa setiap client harus diberi tahu untuk mempercayainya.

Setelah TLS berfungsi, arahkan semua trafik pada port 80 ke port 443:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Tambahkan Strict-Transport-Security hanya jika Anda benar-benar yakin. Header add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; memberi tahu browser agar menolak HTTP biasa untuk hostname tersebut selama dua tahun, dan browser mematuhinya berdasarkan cache lokalnya sendiri. Artinya, menghapus header tersebut kemudian tidak akan membatalkan kebijakan itu. Mulailah dengan max-age selama beberapa jam, pastikan setiap subdomain benar-benar menggunakan HTTPS, lalu tingkatkan durasinya.

FAQ

Apa perbedaan antara HTTP dan HTTPS?

HTTPS adalah HTTP yang dikirim di dalam sesi TLS (transport layer security). Metode dan kode statusnya sama. Perbedaannya, byte dienkripsi antara client dan komponen yang mengakhiri TLS, sedangkan port default berubah dari 80 menjadi 443. Karena handshake TLS selesai sebelum byte HTTP pertama dikirim, kegagalan sertifikat tidak pernah menghasilkan kode status. Karena itu, peringatan sertifikat di browser menampilkan nama error seperti NET::ERR_CERT_COMMON_NAME_INVALID, bukan angka seperti 403.

Mengapa situs saya mengembalikan 502 Bad Gateway?

502 dari nginx berarti nginx tidak dapat memperoleh respons yang dapat digunakan dari upstream yang diproksikannya. Jadi, permintaan pengunjung tidak bermasalah, tetapi ada masalah pada komponen di belakang nginx. Baca /var/log/nginx/error.log. connect() failed (111: Connection refused) while connecting to upstream berarti tidak ada proses yang listening pada alamat dan port di proxy_pass. Jadi, periksa apakah aplikasi sedang berjalan dan terikat pada alamat yang diharapkan. no live upstreams while connecting to upstream berarti semua server dalam blok upstream telah ditandai down setelah kegagalan berulang. Bandingkan dengan 504 Gateway Timeout, yang berarti upstream menerima koneksi, tetapi kemudian gagal memberikan respons dalam proxy_read_timeout.

Mengapa access log saya menampilkan alamat IP yang sama untuk setiap pengunjung?

Karena $remote_addr mencatat alamat yang membuka koneksi TCP. Di balik reverse proxy atau content delivery network, alamat tersebut adalah alamat proxy. Alamat pengunjung dikirim melalui header X-Forwarded-For. Atur proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; pada proxy. Kemudian, pada nginx penerima, atur set_real_ip_from ke rentang alamat proxy dan real_ip_header X-Forwarded-For;. Cantumkan hanya rentang yang Anda kendalikan. Header tersebut berupa teks yang dapat dikirim oleh client mana pun. Jika Anda mempercayainya dari seluruh Internet, pengunjung dapat menentukan alamat yang dicatat dan alamat yang digunakan untuk rate limiting.

Apakah saya perlu mengaktifkan HTTP/2 atau HTTP/3?

HTTP/2 layak diaktifkan. HTTP/2 hanya memerlukan satu direktif pada situs yang sudah menggunakan TLS dan menghapus batas permintaan per koneksi yang dapat memperlambat halaman dengan banyak file kecil. HTTP/3 memberikan peningkatan yang lebih kecil dan kurang pasti. HTTP/3 juga memerlukan port UDP 443 yang terbuka serta build proxy dengan dukungan QUIC. Ingat bahwa browser hanya beralih ke HTTP/3 setelah menerima header Alt-Svc pada respons sebelumnya. Tanpa header tersebut, tidak ada perubahan, apa pun isi baris listen Anda. Tambahkan $server_protocol ke format log dan ukur protokol yang benar-benar dinegosiasikan oleh pengunjung sebelum meluangkan waktu untuk mengaktifkannya.

Apa arti 403 Forbidden ketika file tersebut ada?

Pada situs statis, 403 biasanya disebabkan oleh izin filesystem, bukan aturan HTTP. open() ... failed (13: Permission denied) di /var/log/nginx/error.log berarti user worker nginx tidak dapat membaca file tersebut. Penyebab yang paling umum adalah direktori induk tidak memiliki izin execute untuk user lain, bukan mode file yang salah. directory index of ... is forbidden berarti permintaan mengarah ke direktori yang tidak memiliki file index saat autoindex dinonaktifkan. Aturan deny yang eksplisit dalam blok location yang cocok juga mengembalikan 403. Jadi, baca blok tersebut jika error log tidak memberikan informasi.

#http#https#web-server#headers#http3