You need the
B flag to escape the backreference and the
noescape) to prevent the resulting substitution (ie. the backreference) being doubly encoded. For example:
RewriteRule ^labels/([^/]+)/?$ /labels/$1/releases/ [B,NE,R=301,L,NC]
You will need to clear your browser cache, as the previous (erroneous) 301 will have been cached.
Why does rewriterules see the encoded
%2F as an actual query string delimiter?
Slight typo there I think... you mean
%3F. Yes, you end up getting 2 redirects because...
when you request
RewriteRule pattern matches against the %-decoded URL-path ie.
/labels/label?!. According to your rule,
label?! is then copied into the substitution, resulting in a redirect to
?! does not get re-encoded automatically). Which is a URL-path of
/labels/label and a query string of
!/releases/. That's where the query string comes from.
On the redirected request,
/labels/label matches your
RewriteRule pattern (the query string is ignored at this stage). This time just
label is copied into the substitution, to become
/labels/label/releases/. And then the query string from the request is passed through to the substitution, to result in a second redirect to
B flag escapes the captured pattern. eg.
label?! is escaped to become
NE flag prevents the
% being encoded as
%25 (ie. effectively doubly encoding the backreference). eg.
label%3F%21 would otherwise be encoded as
Aside: mod_rewrite does not automatically encode the first
? in the substitution since it is assumed this starts the query string. However, subsequent
? will get automatically encoded. eg. Given
RewriteRule ^foo$ /bar??? [R,L], a request for
/foo results in a redirect to
/bar?%3f%3f (note the second two
? are URL encoded, but the first one isn't).