I have to say, your question took me on a wild ride!
I couldn’t get path_regex
to work with redir
at first - until I realized that capture groups only work with rewrite
.
First off, you’re using version v2.7.2, so this won’t work:
@find_lang path_regexp ^/article/([a-z]{2})/
As of v2.8.0, if name
is not provided, the name will be taken from the named matcher’s name. For example a named matcher @foo
will cause this matcher to be named foo
.
You’d need to use this instead:
@find_lang path_regexp find_lang ^/article/([a-z]{2})/
Also, check your regex - it expects something like /article/en/?title=etc
, but your requests are actually /article/en?title=etc
.
After a bunch of testing, this was the only way I could get path_regex
capture groups to work for redirection:
My test Caddyfile
:
{
http_port 8080
}
:8080 {
@lang_title {
path_regexp lang_title ^/article/([a-z]{2})
query title=*
}
route @lang_title {
rewrite https://{re.lang_title.1}.other.org/article/{http.request.uri.query.title}
uri query -title
redir {http.request.uri} 301
}
respond "No redirect"
}
I had to rewrite it first (since capture groups only work with rewrite
), strip title
from the query, and only then could I use redir
. Also, note the route
- without it, this won’t work.
So, here’s my test:
$ curl http://localhost:8080/article/en?title=TEST -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> GET /article/en?title=TEST HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Location: https://en.other.org/article/TEST
< Server: Caddy
< Date: Fri, 28 Mar 2025 03:10:02 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Once I got this working, I adapted it to your use case. In my Caddyfile
, :8081
is there to emulate your mine:8080
. If I request English, I get 404
; otherwise, I get 200
.
My Caddyfile
:
{
http_port 8080
}
:8080 {
reverse_proxy localhost:8081 {
@404 status 404
handle_response @404 {
@lang_title {
path_regexp lang_title ^/article/([a-z]{2})
query title=*
}
route @lang_title {
rewrite https://{re.lang_title.1}.other.org/article/{http.request.uri.query.title}
uri query -title
redir {http.request.uri} 301
}
}
}
}
:8081 {
@english path /article/en*
respond @english "English Site Missing" 404
respond "Other Language Site"
}
Testing English (missing language):
$ curl http://localhost:8080/article/en?title=TEST -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> GET /article/en?title=TEST HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Location: https://en.other.org/article/TEST
< Server: Caddy
< Date: Fri, 28 Mar 2025 03:14:03 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Testing French (non-missing language):
$ curl http://localhost:8080/article/fr?title=TEST -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> GET /article/fr?title=TEST HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Length: 19
< Content-Type: text/plain; charset=utf-8
< Date: Fri, 28 Mar 2025 03:14:23 GMT
< Server: Caddy
< Server: Caddy
<
* Connection #0 to host localhost left intact
Other Language Site
Adjust as needed - hope this helps!
I really hope there’s an easier way to do this, and if there is, I’d love to see it - because this took me embarrassingly too long to figure out! 