Rewrite #
URL rewriting adalah teknik mengubah URL dari sisi server sebelum request diproses lebih lanjut. Berbeda dari redirect (yang memberitahu browser untuk membuka URL lain), rewrite terjadi secara internal — browser dan client tidak mengetahui bahwa URL sudah diubah. Mereka tetap melihat URL asli yang mereka minta.
Caddy menyediakan dua cara utama untuk rewrite URL: direktif rewrite yang merupakan shortcut sederhana, dan direktif uri yang memberikan kontrol lebih granular.
Perbedaan Rewrite vs Redirect #
Redirect (eksternal):
Client → GET /lama HTTP/1.1
Server → 301 Moved Permanently
Location: /baru
Client → GET /baru HTTP/1.1 (request baru!)
Server → 200 OK
Terlihat di browser: URL berubah ke /baru
Digunakan untuk: URL yang berubah permanen, SEO canonical URL
Rewrite (internal):
Client → GET /lama HTTP/1.1
Caddy → ubah path secara internal menjadi /baru
Backend → menerima request sebagai /baru
Client → menerima response 200 OK
Terlihat di browser: URL tetap /lama
Digunakan untuk: clean URL, SPA routing, legacy path support
Direktif rewrite
#
Direktif rewrite mengubah URI request (path + query string):
example.com {
# Rewrite path statis
rewrite /old-path /new-path
# Rewrite dengan query string
rewrite /search /api/search?q={query}
# Rewrite semua request ke satu file (SPA pattern)
rewrite * /index.html
file_server { root /var/www/html }
}
Direktif uri
#
Direktif uri memberikan kontrol lebih spesifik terhadap bagian URI:
example.com {
# Strip prefix dari path
uri strip_prefix /api/v1
# /api/v1/users → /users
# Strip suffix dari path
uri strip_suffix .html
# /about.html → /about
# Tambah prefix
uri path_regexp (.*) /app$1
# /users → /app/users
# Ganti bagian path dengan regex
uri path_regexp ^/blog/(.*)$ /posts/$1
# /blog/hello-world → /posts/hello-world
reverse_proxy backend:3000
}
Rewrite untuk Single Page Application (SPA) #
Ini adalah salah satu use case paling umum — React, Vue, Angular semua memerlukan semua route diarahkan ke index.html:
app.example.com {
root * /var/www/app
# Jika file atau direktori ada, sajikan langsung (aset statis)
# Jika tidak ada, arahkan ke index.html (SPA routing)
try_files {path} /index.html
file_server
}
Dengan rewrite secara Eksplisit
#
app.example.com {
root * /var/www/app
# Jangan rewrite untuk file statis yang ada
@static {
file {path}
path *.js *.css *.png *.jpg *.svg *.ico *.woff2 *.map
}
# Rewrite semua route non-statis ke index.html
@spa {
not file {path}
not path /api/*
}
rewrite @spa /index.html
reverse_proxy /api/* backend:3000
file_server
}
Strip Prefix sebelum Reverse Proxy #
Sangat umum di deployment di mana beberapa service di-host di sub-path:
example.com {
# /api/v1/* dikirim ke backend sebagai /*
handle /api/v1/* {
uri strip_prefix /api/v1
reverse_proxy api-backend:8080
}
# /admin/* dikirim ke admin service sebagai /*
handle /admin/* {
uri strip_prefix /admin
reverse_proxy admin-backend:9000
}
# Root: static files
handle {
root * /var/www/html
file_server
}
}
Rewrite dengan Regex — Transformasi Path yang Kompleks #
example.com {
# Rewrite format URL lama ke format baru
# /posts/2024/01/15/hello-world → /blog/hello-world
rewrite * {re.1} {
path_regexp ^/posts/\d{4}/\d{2}/\d{2}/(.+)$ /blog/{re.1}
}
# Atau dengan uri path_regexp
uri path_regexp ^/posts/\d{4}/\d{2}/\d{2}/(.+)$ /blog/$1
root * /var/www/html
file_server
}
Rewrite Query String #
example.com {
# Tambah query parameter
@search path /search
rewrite @search /search?engine=caddy&{query}
# Hapus query string tertentu
# (lebih mudah dengan uri menggunakan placeholder)
reverse_proxy backend:3000
}
try_files — Fallback Pattern
#
try_files adalah cara elegan untuk mencoba beberapa path secara berurutan:
example.com {
root * /var/www/html
# Coba: file asli → file dengan .html → index.html
try_files {path} {path}.html /index.html
# Contoh:
# /about → coba /about, lalu /about.html, lalu /index.html
# /contact.html → coba /contact.html (langsung ada)
# /dashboard → tidak ada, /dashboard.html tidak ada, fallback /index.html
file_server
}
Rewrite Berdasarkan Header atau Query Parameter #
example.com {
# Rewrite berdasarkan versi API dari header
@v1 header X-API-Version "1"
@v2 header X-API-Version "2"
rewrite @v1 /v1{uri}
rewrite @v2 /v2{uri}
reverse_proxy backend:3000
}
# Atau berdasarkan query parameter
example.com {
@mobile query platform=mobile
rewrite @mobile /mobile{path}
file_server { root /var/www }
}
Rewrite untuk Legacy URL Support #
Ketika melakukan migrasi platform, pertahankan URL lama tetap berfungsi:
example.com {
# URL format lama WordPress
# /2024/01/hello-world/ → /blog/hello-world
uri path_regexp ^/(\d{4})/(\d{2})/([^/]+)/?$ /blog/$3
# Format produk lama
# /product.php?id=123 → /products/123
@old-product {
path /product.php
query id=*
}
rewrite @old-product /products/{query.id}
# Index lama
rewrite /index.php /
rewrite /home.html /
reverse_proxy backend:3000
}
Urutan Eksekusi Rewrite #
example.com {
# PENTING: Rewrite dieksekusi dalam urutan yang ditentukan
# Setelah rewrite, directive berikutnya menggunakan path BARU
# Step 1: Strip prefix
handle /api/* {
uri strip_prefix /api
# Step 2: Setelah strip, path adalah /users bukan /api/users
# Ini dikirim ke backend
reverse_proxy backend:8080
}
}
Rewrite untuk Versioning API #
api.example.com {
# Jika tidak ada versi di path, tambahkan /v2 sebagai default
@no-version {
path /users* /products* /orders*
not path /v1/* /v2/* /v3/*
}
rewrite @no-version /v2{uri}
# Caddy sekarang melihat path /v2/users, /v2/products, dll.
# Backend bisa membedakan versi API
reverse_proxy backend:8080
}
Rewrite untuk Pretty URL #
example.com {
root * /var/www/html
# Izinkan akses /artikel/judul-artikel
# Yang sebenarnya ada di /artikel.php?slug=judul-artikel
@pretty path_regexp ^/artikel/([^/]+)/?$
rewrite @pretty /artikel.php?slug={re.1}
# Atau lebih umum: /[kategori]/[slug] → /index.php?cat=[kategori]&slug=[slug]
@cat-slug path_regexp ^/([^/]+)/([^/]+)/?$
rewrite @cat-slug /index.php?cat={re.1}&slug={re.2}
# Aktifkan PHP FastCGI setelah rewrite
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
}
Ringkasan #
rewritemengubah URI secara internal — browser tidak melihat perubahan ini, berbeda dari redirect yang mengubah URL di browser.uri strip_prefix /apiadalah pola yang paling umum saat routing ke backend — hapus prefix path agar backend tidak perlu mengetahui path mounting-nya.try_files {path} /index.htmladalah konfigurasi SPA (React/Vue/Angular) yang paling umum — sajikan file statis jika ada, fallback keindex.htmluntuk client-side routing.- Gunakan
uri path_regexpuntuk rewrite yang kompleks melibatkan pattern matching dan capture groups.- Rewrite dan redirect bisa dikombinasikan: rewrite dulu secara internal, lalu jika perlu redirect ke URL canonical.
- Urutan directive penting — rewrite mengubah path, dan handler berikutnya menggunakan path yang sudah diubah.
← Sebelumnya: CORS Berikutnya: Redirect →
Rewrite dan Canonical URL #
Dalam konteks SEO, rewrite bisa digunakan untuk memastikan satu URL canonical yang konsisten:
example.com {
root * /var/www/html
# Hapus index.html dari URL (canonical: /about bukan /about/index.html)
@index-html path_regexp /index\.html$
rewrite @index-html {re.0}
uri path_regexp /index\.html$ /
# Hapus .html dari URL (canonical: /about bukan /about.html)
@dot-html path_regexp ^(.+)\.html$
rewrite @dot-html {re.1}
# Tapi file .html tetap harus ada di disk
try_files {path} {path}.html /index.html
file_server
}
Dengan konfigurasi ini, /about.html secara internal direwrite ke /about — browser melihat URL bersih, tapi Caddy tetap bisa menemukan file about.html di disk.
Rewrite Berdasarkan Ketersediaan File #
Pola file dalam matcher memungkinkan rewrite kondisional berdasarkan apakah file tersebut benar-benar ada di disk:
example.com {
root * /var/www/html
# Jika file fisik ada, sajikan langsung
# Jika tidak ada, teruskan ke backend (API atau SPA handler)
@file_exists file {path}
handle @file_exists {
file_server
}
handle {
# Tidak ada file fisik — teruskan ke backend
reverse_proxy backend:3000
}
}
Ini adalah pola yang sangat umum untuk aplikasi hybrid: aset statis (JS, CSS, gambar) disajikan langsung oleh Caddy, sementara route aplikasi diteruskan ke backend.