<section id="flag_b"><title>B (escape backreferences)</title>
<p>The [B] flag instructs <directive
module="mod_rewrite">RewriteRule</directive> to escape non-alphanumeric
-characters before applying the transformation.
-</p>
+characters before applying the transformation.</p>
+<p>In 2.4.10 and later, you can limit the escaping to specific characters
+in backreferences by listing them: <code>[B=#?;]</code> </p>
<p><code>mod_rewrite</code> has to unescape URLs before mapping them,
so backreferences are unescaped at the time they are applied.
<p>This escaping is particularly necessary in a proxy situation,
when the backend may break if presented with an unescaped URL.</p>
+<p>An alternative to this flag is using a <directive module="mod_rewrite"
+>RewriteCond</directive> to capture against %{THE_REQUEST} which will capture
+strings in the encoded form.</p>
</section>
<section id="flag_c"><title>C|chain</title>
data_item *cookie; /* added cookies */
int skip; /* number of next rules to skip */
int maxrounds; /* limit on number of loops with N flag */
+ char *escapes; /* specific backref escapes */
} rewriterule_entry;
typedef struct {
/* Optional functions imported from mod_ssl when loaded: */
static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL;
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL;
-static char *escape_uri(apr_pool_t *p, const char *path);
+static char *escape_uri(apr_pool_t *p, const char *path, const char *escapeme);
/*
* +-------------------------------------------------------+
* Escapes a uri in a similar way as php's urlencode does.
* Based on ap_os_escape_path in server/util.c
*/
-static char *escape_uri(apr_pool_t *p, const char *path) {
+static char *escape_uri(apr_pool_t *p, const char *path, const char *escapeme) {
char *copy = apr_palloc(p, 3 * strlen(path) + 3);
const unsigned char *s = (const unsigned char *)path;
unsigned char *d = (unsigned char *)copy;
unsigned c;
while ((c = *s)) {
- if (apr_isalnum(c) || c == '_') {
- *d++ = c;
- }
- else if (c == ' ') {
- *d++ = '+';
+ if (!escapeme) {
+ if (apr_isalnum(c) || c == '_') {
+ *d++ = c;
+ }
+ else if (c == ' ') {
+ *d++ = '+';
+ }
+ else {
+ d = c2x(c, '%', d);
+ }
}
- else {
- d = c2x(c, '%', d);
+ else {
+ const char *esc = escapeme;
+ while (*esc) {
+ if (c == *esc) {
+ d = c2x(c, '%', d);
+ break;
+ }
+ ++esc;
+ }
+ if (!*esc) {
+ *d++ = c;
+ }
}
++s;
}
/* escape the backreference */
char *tmp2, *tmp;
tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span);
- tmp2 = escape_uri(pool, tmp);
+ tmp2 = escape_uri(pool, tmp, entry->escapes);
rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'",
tmp, tmp2));
case 'B':
if (!*key || !strcasecmp(key, "ackrefescaping")) {
cfg->flags |= RULEFLAG_ESCAPEBACKREF;
+ if (val && *val) {
+ cfg->escapes = val;
+ }
}
else {
++error;