Skip to content

Commit 4e94de2

Browse files
docs: update htaccess migration guide with new conditions syntax
Refactor documentation examples to use the idiomatic `condition` and `if` blocks instead of `location` for path-based logic, ensuring all examples adhere to the current Ferron configuration style.
1 parent c0d82be commit 4e94de2

1 file changed

Lines changed: 35 additions & 17 deletions

File tree

docs/migration/from-htaccess.md

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ example.com {
7474
header "Referrer-Policy" "strict-origin-when-cross-origin"
7575
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
7676
77+
condition WP_ADMIN {
78+
request.uri ~ r"/wp-login\.php|/wp-admin(?:/|$)"
79+
}
80+
7781
# Protect the admin area by IP.
78-
location /wp-admin {
82+
if WP_ADMIN {
7983
allow "203.0.113.0/24"
8084
block "0.0.0.0/0"
8185
}
@@ -249,7 +253,7 @@ example.com {
249253
}
250254
```
251255

252-
Place these inside a `location` block to protect a single path (such as `/wp-admin` or `/administrator`).
256+
Place these inside a `location` or `if` block to protect a single path (such as `/wp-admin` or `/administrator`).
253257

254258
> [!important]
255259
> If Ferron sits behind a reverse proxy or load balancer, configure `client_ip_from_header` with a `trusted_proxy` list so the IP rules evaluate the real client IP rather than the proxy's address.
@@ -387,7 +391,11 @@ Ferron's `basic_auth` does **not** read `htpasswd` files. It expects **hashed**
387391
example.com {
388392
root /var/www/html
389393
390-
location /wp-admin {
394+
condition WP_ADMIN {
395+
request.uri ~ r"/wp-login\.php|/wp-admin(?:/|$)"
396+
}
397+
398+
if WP_ADMIN {
391399
basic_auth {
392400
realm "Admin Area"
393401
users {
@@ -401,16 +409,18 @@ example.com {
401409
Brute-force protection is enabled by default; you can tune it:
402410

403411
```ferron
404-
basic_auth {
405-
realm "Admin Area"
406-
users {
407-
admin "$argon2id$v=19$m=19456,t=2,p=1$..."
408-
}
409-
brute_force_protection {
410-
enabled
411-
max_attempts 5
412-
lockout_duration "15m"
413-
window "5m"
412+
example.com {
413+
basic_auth {
414+
realm "Admin Area"
415+
users {
416+
admin "$argon2id$v=19$m=19456,t=2,p=1$..."
417+
}
418+
brute_force_protection {
419+
enabled
420+
max_attempts 5
421+
lockout_duration "15m"
422+
window "5m"
423+
}
414424
}
415425
}
416426
```
@@ -436,7 +446,11 @@ example.com {
436446
root /var/www/html
437447
mime_type ".pdf" "application/octet-stream"
438448
439-
location /downloads {
449+
match DOWNLOADS {
450+
request.uri.path ~ r"^/downloads(?:/|$)"
451+
}
452+
453+
if DOWNLOADS {
440454
header +Content-Disposition "attachment"
441455
}
442456
}
@@ -454,15 +468,19 @@ Apache uses `mod_expires`:
454468
</IfModule>
455469
```
456470

457-
Ferron does not compute `Expires` from durations, but you can set `Cache-Control` directly and let the client derive expiry. Use `file_cache_control` for all static files and `header` for per-type overrides (matching by `location` or `match`):
471+
Ferron does not compute `Expires` from durations, but you can set `Cache-Control` directly and let the client derive expiry. Use `file_cache_control` for static files:
458472

459473
```ferron
460474
example.com {
461475
root /var/www/html
462476
file_cache_control "public, max-age=604800" # 1 week default
463477
464-
location /assets {
465-
header +Cache-Control "public, max-age=2592000" # 30 days
478+
match ASSETS {
479+
request.uri.path ~ r"^/assets(?:/|$)"
480+
}
481+
482+
if ASSETS {
483+
file_cache_control "public, max-age=2592000" # 30 days
466484
}
467485
}
468486
```

0 commit comments

Comments
 (0)