How to make a rewrite rule of "Redirect Trailing Slashes If Not A Folder."


(fanybook) #1

Like this :

# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

I tried, but
caddy’s rewrite can’t use 301
caddy’s redir can’t use regexp :frowning:
caddy’s rewrite & redir can’t -d -f

caddy can’t 301 redir when the path is not a folder and has trailing slash

in apache, redir & rewrite both use RewriteCond + RewriteRule


(Matthew Fay) #2

To clarify, you want to strip trailing slashes?

You can probably combine rewrite and redir, I think… Try something like this:

# First we rewrite, trying
# the request without the
# trailing slash in case it's
# actually a file.
rewrite {
  regexp ^(.+)/$
  to {1} {1}/
}

# Then, if the request ended
# in a slash, but the rewrite
# didn't, redirect to the new URI
redir 301 {
  if {uri} ends_with /
  if {rewrite_uri} not_ends_with /
  / {rewrite_uri}
}

(fanybook) #3

thank you very much~

it works

rewrite {
    regexp ^(.+)/$
    to {1} {1}/
}

rewrite {
    to {path} /index.php?{query}
}


redir 301 {
    if {uri} ends_with /
    if {rewrite_uri} not_ends_with /
    / {rewrite_uri}
}

(fanybook) #4

And there’s an interesting question.

/index.php?/api/circle/group?limit=2   can't work

but

rewrite {
    to {path} /index.php?{query}
}

after rewrite 
/api/circle/group?limit=2   can work

It‘s ’’/index.php?limit=2’ after rewrite, isn’t it? What is the content of {query} ?

I think it should be like this

rewrite {
    to {path} /index.php{uri}
}
/api/circle/group?limit=2

(Matthew Fay) #5

After this rewrite, if the path is invalid, {rewrite_uri} will be /index.php?/api/circle/group?limit=2.

The query is everything after the first ? and before any anchors. In this case, {query} is /api/circle/group?limit=2.

You can effectively test these for yourself by adding log / stdout "{uri} {rewrite_uri} {path} {query}" or similar to your Caddyfile (adding or removing placeholders as you need), initiating requests, and observing the output from Caddy.


(fanybook) #6

I have not expressed myself very well.

{host}/index.php?/api/circle/group?limit=2   can't work

but

after rewrite 
{host}/api/circle/group?limit=2   can work

I think the {query} is limit=2, {uri} is /api/circle/group?limit=2, {path} is /api/circle/group
If what I think is right, /index.php?{query} is ’/index.php?limit=2’
In this case, if {query} is /api/circle/group?limit=2, rewrite to /index.php?/api/circle/group?limit=2 should can’t work, because of two ?


(Matthew Fay) #7

Sorry, yes, this is a correct assessment for the given request /api/circle/group?limit=2.

My above post was the results from a request for /index.php?/api/circle/group?limit=2.

No, the second ? won’t interfere. There can be question marks in the {query} placeholder. The first question mark is regarded as the beginning of the query, and it ends only at the end of the URI or the start of the fragment #.


(fanybook) #8

Now it doesn’t work.

# First we rewrite, trying
# the request without the
# trailing slash in case it's
# actually a file.
rewrite {
  regexp ^(.+)/$
  to {1} {1}/
}

# Then, if the request ended
# in a slash, but the rewrite
# didn't, redirect to the new URI
redir 301 {
  if {uri} ends_with /
  if {rewrite_uri} not_ends_with /
  / {rewrite_uri}
}

if rewrite’s result is 404, redir wasn’t executed.


(Matthew Fay) #9

Yes, that is the expected behaviour of that configuration.

That redirect only ever takes place if the client requested a URL with a trailing slash, but the rewrite found a file without the trailing slash was present.

If there’s no file (i.e. a 404 would be generated), the rewrite falls back to the originally-requested URI, and if that URI doesn’t exist, the file server will naturally return the 404.


(fanybook) #10

how to redir when the rewrite found a php script without the trailing slash was present.

Now it doesn’t work.

rewrite {
    regexp ^(.+)/$
    to {1} {1}/
}

rewrite {
    to {path} /index.php?{query}
}


redir 301 {
    if {uri} ends_with /
    if {rewrite_uri} not_ends_with /
    / {rewrite_uri}
}

(Matthew Fay) #11

Your configuration already achieves this.

The Caddyfile example you posted, however, has a second rewrite. That rewrite will only ever occur if the first rewrite’s regex isn’t matched - that is, it’ll only work on requests that don’t end in a trailing slash.

If you want your first rewrite to also fall back to /index.php?{query}, you’ll need to add it to the list of targets on the first rewrite.


(fanybook) #12

modified rule, but {rewrite_uri} be changed to /index.php?xxxxxxxxxx

rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}
}

rewrite {
    to {uri} /index.php?{uri}
}

redir 301 {
    if {uri} ends_with /
    if {rewrite_uri} not_ends_with /
    / {rewrite_uri}
}

how to remove index.php?


(fanybook) #13

how to remove index.php?


(Matthew Fay) #14

I’m not sure what you mean, sorry. What do you want to remove index.php? from, exactly?


(fanybook) #15

eg. xxx.com/abc/ddddddd/
this url with a trailing slash, there’s no file, but php route is exist.
① match it,{rewrite_uri} is xxx.com/index.php?/abc/ddddddd
③ match it, this url is redirected, the new url is xxx.com/index.php?/abc/ddddddd after redirect

①rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}
}

②rewrite {
    to {uri} /index.php?{uri}
}

③redir 301 {
    if {uri} ends_with /
    if {rewrite_uri} not_ends_with /
    / {rewrite_uri}
}

(Matthew Fay) #16

This shouldn’t ever happen, unless you have a different redir in your Caddyfile somewhere.

The ③ redirect won’t redirect the rewritten URI if the URI ends in a slash (if {rewrite_uri} not_ends_with /).


(fanybook) #17

But the result is that.

my rule like above,only three(2rewrite 1redir)

xxx.com/abc/ddddddd/ was redirected xxx.com/index.php?/abc/ddddddd when php route exists


(Matthew Fay) #18

Ahh, I figured out the problem, I think!

Double check whether you’re being redirected to xxx.com/index.php?/abc/ddddddd/ - I’ve got a feeling you’re actually getting redirected to xxx.com/index.php?/abc/ddddddd instead (note the lack of trailing slash).

Here’s why:

  rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}
  }

Notice how your regexp capture group excludes the trailing slash? You’re not putting the original URI back on the end, you’re putting it on sans trailing slash, and thus the redirect fires in the event of the fallback rewrite. Change it to:

  rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}/
  }

And let me know if that solves the issue.


(fanybook) #19
rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}/
}

the issue has not been solved.

/index.php?{1}/ can’t remove trailing slashes, no effect (because of {rewrite_uri} ends_with /, no redir)

I remember, in old version, there was no such problem (rule like these)


Rectify the above execute results.

xxx.com/abc/ddddddd/ was redirected xxx.com/index.php?/abc/ddddddd when php route exists
when rule like these:

①rewrite {
    regexp ^(.+)/$
    to {1} {1}/ /index.php?{1}
}

②rewrite {
    to {uri} /index.php?{uri}
}

③redir 301 {
    if {uri} ends_with /
    if {rewrite_uri} not_ends_with /
    / {rewrite_uri}
}

(Matthew Fay) #20

Perhaps my understanding of your desired behaviour was incorrect.

If you want to strip the trailing slash from the PHP index, and redirect the client, use: /index.php?{1}

If you don’t want to strip the trailing slash from the PHP index, and therefore not redirect the client, use: /index.php?{1}/ OR /index.php?{uri}

If you want to strip the trailing slash from the PHP index, but not redirect the client, use either form above on the rewrite, and add another condition to your redirect: if {rewrite_uri} not_starts_with /index.php?