Matcher #

Secara default, setiap directive yang kita tulis di dalam sebuah site block Caddyfile akan diterapkan ke seluruh request HTTP yang masuk ke situs tersebut. Misalnya, jika kita menulis reverse_proxy localhost:3000, maka setiap request, baik itu ke halaman utama /, aset gambar /logo.png, maupun endpoint API /api/v1/users, akan diteruskan ke server backend tersebut.

Namun, dalam skenario dunia nyata, kita jarang menginginkan perilaku seragam seperti itu. Kita sering kali membutuhkan logika bersyarat, seperti: “aktifkan kompresi untuk seluruh berkas kecuali gambar”, “minta autentikasi password hanya untuk rute /admin/*”, atau “teruskan request ke server backend hanya jika request tersebut menuju jalur /api/”. Di sinilah Request Matcher (pencocok permintaan) memainkan peran yang sangat vital. Matcher adalah mekanisme bawaan Caddy yang memungkinkan kita memfilter dan mengarahkan lalu lintas data secara granular berdasarkan berbagai properti request.


Logika Evaluasi Request Matcher #

Caddy mengevaluasi request yang masuk menggunakan serangkaian filter matcher sebelum menerapkan directive terkait.

Untuk memvisualisasikan alur pencocokan request ini, mari kita perhatikan diagram alur (flowchart) evaluasi berikut:

flowchart TD
    Request["Request Masuk (GET /api/users)"] --> EvalHost{"1. Evaluasi Host Matcher?"}
    EvalHost -- "Tidak Cocok" --> Ignore["Abaikan Block / Lewati"]
    EvalHost -- "Cocok" --> EvalPath{"2. Evaluasi Path Matcher?"}
    EvalPath -- "Tidak Cocok" --> Ignore
    EvalPath -- "Cocok" --> EvalMethod{"3. Evaluasi Method Matcher?"}
    EvalMethod -- "Tidak Cocok" --> Ignore
    EvalMethod -- "Cocok" --> CheckCEL{"4. Evaluasi Ekspresi CEL (Jika ada)?"}
    CheckCEL -- "Evaluasi False" --> Ignore
    CheckCEL -- "Evaluasi True" --> ApplyDirective["Terapkan Directive ke Request"]

Dua Cara Menulis Matcher #

Caddyfile mendukung dua gaya penulisan matcher sesuai dengan tingkat kompleksitas kondisi yang kita butuhkan:

1. Inline Matcher (Langsung di Directive) #

Inline matcher ditulis langsung pada baris yang sama dengan directive. Ini adalah cara tercepat dan paling bersih untuk kondisi pencocokan sederhana yang hanya melibatkan URL path.

example.com {
    # Matcher inline '/api/*' membatasi reverse_proxy hanya untuk path tersebut
    reverse_proxy /api/* localhost:8080
    
    # Layani berkas statis untuk sisa request lainnya
    file_server
}

[!NOTE] Semua inline matcher di Caddy secara default dianggap sebagai Path Matcher jika diawali dengan karakter slash (/) atau bintang (*).

2. Named Matcher (Matcher Bernama) #

Named matcher didefinisikan secara terpisah menggunakan karakter @ diikuti dengan nama kustom yang kita tentukan. Named matcher sangat berguna untuk mendefinisikan kondisi kompleks yang melibatkan beberapa parameter (seperti gabungan path, method, IP address, dan header) atau jika kondisi tersebut ingin kita gunakan kembali di beberapa directive berbeda.

example.com {
    # Definisi named matcher '@aksesAdmin'
    @aksesAdmin {
        path /admin/*
        method GET POST
        not remote_ip 192.168.1.0/24
    }
    
    # Gunakan matcher tersebut dalam directive basicauth
    basicauth @aksesAdmin {
        admin $2a$14$hashcontoh...
    }
}

Named Matcher vs Inline Matcher: Kapan Memilih Yang Mana? #

Memilih gaya penulisan matcher yang tepat sangat menentukan tingkat keterbacaan (maintainability) berkas Caddyfile kita seiring bertambahnya domain dan logika aplikasi.

Kapan Menggunakan Inline Matcher? #

  • Rute Tunggal Sederhana: Ketika kita hanya perlu melakukan pencocokan berdasarkan URL path (misalnya /images/* atau /health).
  • Sintaks Ringkas: Menghindari pembuatan blok baru jika rutenya sangat sederhana, sehingga berkas Caddyfile tetap pendek.

Kapan Menggunakan Named Matcher? #

  • Kombinasi Multikriteria: Ketika kita perlu mencocokkan request berdasarkan IP address dan method HTTP sekaligus (misalnya “Hanya menerima method POST dari IP Kantor”).
  • Prinsip DRY (Don’t Repeat Yourself): Ketika kondisi yang sama harus digunakan oleh beberapa directive berbeda secara bersamaan. Sebagai contoh, kita ingin mendeteksi request API untuk mengaktifkan kompresi, menambahkan header CORS, dan mengarahkan proxy:
example.com {
    # Mendefinisikan matcher sekali saja
    @requestApi {
        path /api/*
        header Accept application/json
    }

    # Menggunakan kembali matcher '@requestApi' di 3 directive berbeda
    header @requestApi Access-Control-Allow-Origin "https://app.example.com"
    encode @requestApi gzip
    reverse_proxy @requestApi localhost:8080
}

Daftar Matcher Standar yang Tersedia #

Caddy menyediakan banyak jenis matcher bawaan yang siap digunakan untuk menyaring request berdasarkan berbagai aspek protokol HTTP:

1. path & path_regexp (Mencocokkan URL Path) #

Mencocokkan request berdasarkan jalur URL yang diminta oleh browser client.

# Cocok dengan path eksak '/masuk' saja
@exact path /masuk

# Cocok dengan prefix '/static/' (menggunakan wildcard '*')
@static path /static/*

# Cocok dengan beberapa path sekaligus
@assets path /css/* /js/* /images/*

# Cocok dengan ekstensi berkas tertentu
@phpFiles path *.php

Jika pencocokan wildcard standar kurang fleksibel, kita dapat menggunakan path_regexp untuk menggunakan ekspresi reguler (Regular Expressions):

# Cocok dengan rute /users/[angka]/profile
@userProfile path_regexp user ^/users/([0-9]+)/profile$

# Kita dapat menggunakan variabel capture group di directive lain
rewrite @userProfile /profile-handler?user_id={re.user.1}

2. host (Mencocokkan Hostname/Domain) #

Sangat berguna di dalam blok situs yang melayani banyak domain sekaligus (multi-domain site block).

example.com, api.example.com, admin.example.com {
    @isApi host api.example.com
    @isAdmin host admin.example.com
    
    # Rute traffic berdasarkan host matcher
    reverse_proxy @isApi localhost:8080
    reverse_proxy @isAdmin localhost:9000
    
    # Default untuk example.com
    file_server
}

3. method (Mencocokkan Method HTTP) #

Menyaring lalu lintas berdasarkan tindakan HTTP yang dikirimkan oleh browser (seperti GET, POST, PUT, DELETE).

# Hanya cocok jika method-nya POST
@postRequest method POST

# Cocok dengan operasi pembacaan data saja
@readOps method GET HEAD

Menyaring request berdasarkan keberadaan atau nilai dari header HTTP tertentu, termasuk Session dan Cookie.

# Cocok jika header Authorization mengandung nilai apa pun (wildcard '*')
@hasToken header Authorization *

# Cocok jika request meminta format JSON
@expectsJSON header Accept application/json

# Cocok jika browser yang digunakan adalah Google Chrome
@isChrome header User-Agent *Chrome*

# Mencocokkan cookie tertentu (Caddy membaca Cookie sebagai header HTTP)
@sessionAktif header Cookie *session_id=*

5. remote_ip (Mencocokkan Alamat IP Client) #

Membatasi akses berdasarkan alamat IP pengirim request. Mendukung penulisan IP tunggal maupun format CIDR.

# Cocok jika IP berasal dari jaringan lokal (LAN)
@jaringanInternal remote_ip 10.0.0.0/8 192.168.1.0/24

# Jika Caddy berada di belakang Cloudflare/Load Balancer, gunakan opsi 'forwarded'
# agar Caddy membaca IP asli client dari header X-Forwarded-For
@clientTepercaya remote_ip forwarded 203.0.113.50

6. query (Mencocokkan Parameter Query) #

Mencocokkan request berdasarkan query string yang disertakan di ujung URL.

# Cocok jika URL mengandung '?debug=true'
@debugMode query debug=true

# Cocok jika parameter 'page' ada dengan nilai apa pun
@paginated query page=*

7. file (Mencocokkan Keberadaan File Fisik) #

Memeriksa apakah file yang diminta oleh client benar-benar ada di disk server sebelum memutuskan tindakan berikutnya.

example.com {
    root * /var/www/html
    
    # Cocok jika file yang diminta TIDAK ADA di disk
    @fileNotFound {
        not file {path}
    }
    
    # Tulis ulang request ke index.html jika file tidak ditemukan (SPA Routing)
    rewrite @fileNotFound /index.html
    
    file_server
}

8. protocol (Mencocokkan HTTP Protocol) #

Menyaring request berdasarkan versi protokol yang digunakan oleh client.

# Mengidentifikasi request WebSocket
@isWebSocket {
    protocol http
    header Upgrade websocket
}

# Mengidentifikasi request HTTP/3 (QUIC)
@isHTTP3 {
    protocol h3
}

Matriks Ringkasan Kapabilitas Matcher Standar #

Berikut adalah tabel ringkasan pembanding kapabilitas dari setiap request matcher utama di Caddy:

Nama MatcherEvaluasi UtamaContoh SintaksisKegunaan Utama di Production
pathURL Path Stringpath /api/*Perutean URL tingkat tinggi.
path_regexpRegex URL Pathpath_regexp ^/user/\d+$Ekstraksi parameter ID dari URL.
hostDomain HTTP Hosthost api.domain.comPerutean multi-domain dalam satu blok.
methodHTTP Methodmethod GET POSTPembatasan rute tulis/baca aplikasi.
headerHTTP Header Key/Valueheader Cookie *session*Pemeriksaan header keamanan dan sesi cookie.
remote_ipIP Address & CIDRremote_ip 10.0.0.0/8IP whitelisting dan pembatasan intranet.
queryURL Query Parametersquery debug=trueDeteksi parameter penelusuran masalah.
fileDisk Filesystemfile {path}Fallback router untuk Single Page App (SPA).
protocolHTTP Version / TLSprotocol httpsPenanganan khusus lalu lintas WebSocket / HTTP3.

Operator Logika: NOT, AND, OR #

Secara default, jika kita menuliskan beberapa kondisi di dalam satu blok named matcher, Caddy akan mengevaluasinya dengan logika AND (semua kondisi wajib terpenuhi agar matcher menghasilkan nilai true).

Namun, kita dapat menyusun logika yang lebih kompleks menggunakan operator berikut:

1. Operator not (Negasi/Kebalikan) #

Membalikkan hasil evaluasi dari kondisi di dalamnya.

# Cocok jika path BUKAN diawali dengan /public/
@bukanPublik {
    not path /public/*
}

# Cocok jika IP pengirim BUKAN dari LAN
@luarKantor {
    not remote_ip 10.0.0.0/8
}

2. Logika OR (Salah Satu Terpenuhi) #

Untuk menguji kondisi OR, kita dapat mendefinisikan beberapa named matcher terpisah lalu mengarahkannya ke directive yang sama, atau menggunakan Expression Matcher berbasis bahasa CEL.

# Opsi A: Menggunakan beberapa named matcher terpisah
@ruteAdmin path /admin/*
@ruteSuper path /superuser/*

# Kedua rute dilindungi oleh basicauth yang sama
basicauth @ruteAdmin { ... }
basicauth @ruteSuper { ... }

Ekspresi CEL (Common Expression Language) #

Untuk logika pengkondisian tingkat lanjut yang sangat rumit (seperti operasi string, logika OR bersarang, atau pemeriksaan variabel runtime dinamis), Caddy menyediakan matcher expression yang menggunakan parser CEL (Common Expression Language).

example.com {
    # Kondisi kompleks: Hanya cocok jika method POST, tipe konten JSON,
    # dan berasal dari IP di luar jaringan kantor
    @kondisiKhusus {
        expression {
            request.method == "POST" &&
            request.header["Content-Type"][0] == "application/json" &&
            !req_ip("10.0.0.0/8")
        }
    }
    
    reverse_proxy @kondisiKhusus localhost:8080
}

Beberapa fungsi CEL yang sering berguna di produksi:

  • request.uri.path.startsWith('/api/') — Memeriksa awal path.
  • request.header['User-Agent'][0].contains('bot') — Deteksi bot.
  • req_ip('192.168.1.0/24') — Pencocokan IP dinamis.

Pola Penerapan Matcher di Production #

Berikut adalah beberapa template implementasi matcher yang sangat berguna untuk mengamankan dan merapikan konfigurasi server produksi kita:

1. Pembagian Traffic API vs Frontend (Single Domain) #

Sering kali kita ingin menyajikan frontend statis dan API backend menggunakan satu domain yang sama untuk menghindari isu CORS.

example.com {
    # 1. Definisikan Matcher untuk API dan Aset Statis
    @jalurApi path /api/*
    @asetStatis path /static/* /assets/*
    
    # 2. API diteruskan ke backend aplikasi
    reverse_proxy @jalurApi localhost:8000
    
    # 3. Aset statis dilayani dari folder khusus
    file_server @asetStatis {
        root /var/www/app/static
    }
    
    # 4. Sisa request lainnya diarahkan ke SPA React/Vue
    @spaFallback {
        not path /api/*
        not file {path}
    }
    rewrite @spaFallback /index.html
    file_server {
        root /var/www/app/dist
    }
}

2. Pemblokiran Bot Jahat (Scraper/Crawler) secara Dinamis #

Kita dapat memblokir akses ke situs kita untuk bot pengikis data (scraper) yang membebani server menggunakan kombinasi header User-Agent.

example.com {
    # Matcher ekspresi untuk mendeteksi User-Agent bot jahat
    @botJahat {
        expression {
            request.header["User-Agent"][0].contains("scrapy") ||
            request.header["User-Agent"][0].contains("wget") ||
            request.header["User-Agent"][0].contains("curl")
        }
    }
    
    # Kirim status 403 Forbidden secara instan tanpa memproses request ke disk/backend
    respond @botJahat "Akses Ditolak" 403
    
    reverse_proxy localhost:3000
}

3. Layered Security (Keamanan Berlapis) untuk Admin Dashboard #

Membatasi akses halaman admin agar hanya dapat dibuka dari jaringan internal kantor (IP Whitelisting) dan tetap meminta verifikasi password.

admin.company.com {
    # Matcher 1: Deteksi request yang berasal dari LUAR jaringan kantor
    @luarKantor {
        not remote_ip 10.0.0.0/8 192.168.1.0/24
    }
    
    # Blokir akses dari luar kantor dengan status 403
    respond @luarKantor "Unauthorized Network Access" 403
    
    # Matcher 2: Semua request wajib melewati basicauth
    basicauth {
        spv $2a$14$hashspv...
    }
    
    reverse_proxy localhost:9000
}

Debugging Request Matcher #

Jika konfigurasi matcher kita tidak bekerja seperti yang kita harapkan (misalnya rute tidak cocok atau request terblokir padahal seharusnya diizinkan), kita dapat melakukan debug menggunakan dua metode berikut:

1. Gunakan Perintah caddy adapt #

Adaptasi Caddyfile Anda ke JSON untuk melihat bagaimana logika AND/OR dan negasi diubah menjadi objek matcher internal. Cek apakah ada struktur bersarang yang salah.

caddy adapt --config Caddyfile | jq .apps.http.servers

Hasil adaptasi JSON akan menunjukkan bagaimana named matcher diterjemahkan menjadi array match terstruktur, yang mempermudah kita mendeteksi kesalahan sintaks logika logis.

2. Tambahkan Directive respond Sementara #

Untuk memastikan apakah request Anda benar-benar tertangkap oleh matcher tertentu, letakkan directive respond sementara di bawah matcher tersebut:

example.com {
    @testMatcher {
        path /uji-coba/*
        method POST
    }
    
    # Kembalikan teks uji coba secara instan untuk verifikasi
    respond @testMatcher "Kondisi Matcher Terpenuhi!" 200
    
    reverse_proxy localhost:3000
}

Jika Anda mengakses /uji-coba/ menggunakan POST dan menerima teks tersebut, berarti matcher Anda sudah berjalan dengan benar. Setelah pengujian selesai, Anda dapat menghapus baris respond tersebut.


Ringkasan #

  • Request Matcher digunakan untuk menerapkan directive secara kondisional hanya pada kriteria request tertentu.
  • Inline Matcher (seperti /api/*) sangat cocok untuk penyaringan rute path sederhana di baris yang sama dengan directive.
  • Named Matcher diawali dengan simbol @ (misal @akses) untuk menyusun kombinasi filter yang kompleks dan terstruktur.
  • Kondisi-kondisi di dalam satu blok named matcher secara bawaan dievaluasi menggunakan logika AND.
  • Gunakan operator not untuk membalikkan kondisi pencocokan (seperti memblokir IP atau mengecualikan file).
  • Matcher expression berbasis bahasa CEL menyediakan fleksibilitas pemrograman penuh untuk logika perutean tingkat lanjut di Caddy.

← Sebelumnya: Directive   Berikutnya: Snippet & Import →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact