Rewrite rule fails to match single quote chars - mod-rewrite

I asked this question earlier:
mod_rewrite: match only if no previous rules have matched?
And have been using the suggested solution with success for a while now:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{THE_REQUEST} ^.+\ (/[^?\s]*)\??([^\s]*)
RewriteCond %{REQUEST_URI}?%{QUERY_STRING}<%1?%2 ^([^<]*)<\1$
RewriteRule .* /pub/dispatch.php [L]
However, we've since discovered that this rule fails for URLs containing single quote chars, e.g. http://example.com/don't_do_it (which is actually requested as http://example.com/don%27t_do_it)
Specifically, this is the line that's failing to match:
RewriteCond %{REQUEST_URI}?%{QUERY_STRING}<%1?%2 ^([^<]*)<\1$
commenting it out causes the rule to match as expected, but breaks the "match only if no previous rules have matched" behavior. This is presumably related to the fact that ' is urlencoded to %27.
Here's the relevant RewriteLog entry (for the url /asdf'asdf aka /asdf%27asdf):
RewriteCond: input='/asdf'asdf?</asdf%27asdf?' pattern='^([^<]*)<\1$' => not-matched
What I'm seeing here is that %{REQUEST_URI} is unescaped while %{QUERY_STRING} is escaped, hence the mismatch. Is there an alternative to either one of those I should be using?
Any ideas how to rewrite the above line so that it will also match lines that contain ' chars?

Try the C flag and chain the sequence of rules of which you just want one to be applied. So actually chain all of your rules.

You can test the [NE] flag at the end of the RewriteRule.

After beating on it for quite some time, things are looking good with:
RewriteMap unescape int:unescape
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{THE_REQUEST} ^.+\ (/[^?\s]*)\??([^\s]*)
RewriteCond %{REQUEST_URI}?%{QUERY_STRING}<${unescape:%1}?%2 ^([^<]*)<\1$
RewriteRule .* /pub/dispatch.php [L]

Related

Mod rewrite rule for mapping one or two parameters

I have the following .htaccess:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?([^/]+)/?$ /?page=$1 [L,QSA]
RewriteRule ^/?([^/]+)/([^/]+)/?$ /?page=$1&id=$2 [L,QSA]
This rules allow to enter urls like:
example.com/my-account-dashboard
example.com/my-account-dashboard/1
which are pretty urls for:
example.com?page=my-account-dashboard
example.com?page=my-account-dashboard&id=1
This works fine so far. But internaly the links are with those parameters. Is it possible to redirect (or something) to the pretty urls if possible? What are the rewrite rules for that?
First of all, a few remarks about your current code which contains some errors.
1) RewriteCond only applies on the very next RewriteRule. So your second RewriteRule can match without that condition (you can try it, you'll see). You need to put (again) that condition to the other RewriteRule (or use S skip flag to simulate if/else condition but it gets complicated for nothing).
2) I'm pretty sure you don't want to use QSA flag the way you do. By using it, you tell mod_rewrite to append any query string to the rewrite. Example: example.com/my-account-dashboard/?foo=bar will rewrite to /?page=my-account-dashboard&foo=bar. So unless you really want that, you don't need it. A lot of people think that they need QSA when adding some query string directly in the rewrite, just like you do. Again, this is not an error that will make everything crash, but still it's not totally correct.
3) Your rules create duplicate content which is bad for SEO (referencing). For instance, example.com/my-account-dashboard and example.com/my-account-dashboard/ (notice the trailing slash) both lead to the same page. But search engines won't consider them as the same. I invite you to search "duplicate content" on Google (or any other search engine you like) and have a look at it. A simple way to avoid this is to chose either with or without the trailing slash.
Now that the base is clear, let's answer to your question. You can't simply use a redirect R from old-url to new-url because you'd end up with an infinite loop. Something is there for this problem: THE_REQUEST. When mod_rewrite uses it, it is able to know that it comes from a direct client request, not a redirect/rewrite by itself.
All-in-one, here is how your code should look like:
RewriteEngine On
RewriteBase /
# Redirect old-url /?page=XXX to new-url equivalent /XXX
RewriteCond %{THE_REQUEST} \s/\?page=([^/&\s]+)\s [NC]
RewriteRule ^ /%1? [R=301,L]
# Redirect old-url /?page=XXX&id=YYY to new-url equivalent /XXX/YYY
RewriteCond %{THE_REQUEST} \s/\?page=([^/&\s]+)&id=([0-9]+)\s [NC]
RewriteRule ^ /%1/%2? [R=301,L]
# if /XXX is not a file/directory then rewrite to /?page=XXX
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?([^/]+)$ /?page=$1 [L]
# if /XXX/YYY is not a file/directory then rewrite to /?page=XXX&id=YYY
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?([^/]+)/([0-9]+)$ /?page=$1&id=$2 [L]
NB: i chose to use the "without trailing slash" option (e.g. example.com/my-account-dashboard and example.com/my-account-dashboard/1). Feel free to ask if you want with.

mod_rewrite with trailing period in URL

I have a RewriteRule on my Apache to make URLs friendly
RewriteRule ^log/(.+)$ script.php?u=$1 [QSA]
This makes http://example.com/log/username get internally redirected to http://example.com/script.php?u=username
This works fine, as long as the username does not contain a trailing period. However, there are usernames, where people chose names like "firstname-L." (note the trailing period)
In this case http://example.com/firstname-L. gets translated to http://example.com/script.php?u=firstname-L (the trailing period is gone)
How can I get this to work?
You can try to check these configuration directives:
RewriteCond %{REQUEST_URI} !^/log/(.+)\.$
RewriteRule ^log/(.+)$ /script.php?u=$1 [QSA]
RewriteCond %{REQUEST_URI} ^/log/(.+)\.$
RewriteRule ^log/(.+)$ /script.php?u=$1. [QSA]
OR:
RewriteCond %{REQUEST_URI} ^/log/(.+)([a-zA-Z0-9_-]{1})$
RewriteRule ^log/(.+)$ /script.php?u=$1 [QSA]
RewriteCond %{REQUEST_URI} ^/log/(.+)\.$
RewriteRule ^log/(.+)$ /script.php?u=$1. [QSA]
I hope, I'm not mistaken.. Now, as the variable of usernames with one character or one character with trailing period won't work in the second code, we can try to replace the second code with this one:
RewriteCond %{REQUEST_URI} ^/log/(.*)([a-zA-Z0-9_-]{1})$
RewriteRule ^log/(.+)$ /script.php?u=$1 [QSA]
RewriteCond %{REQUEST_URI} ^/log/(.*)\.$
RewriteRule ^log/(.*)$ /script.php?u=$1. [QSA]
Or you could try these three liner directives if you doesn't like the four liner:
RewriteCond %{REQUEST_URI} ^/log/(.*)\.$
RewriteRule ^log/(.*)$ /script.php?u=$1. [QSA]
RewriteRule ^log/(.*)([a-zA-Z0-9_-]{1})$ /script.php?u=$1$2 [QSA]
I hope, all these codes above will work!
Your rule appears to be right, perhaps the client or server is stripping the last dot as spurious (checked on YT with Chrome, if you add a dot at the end of a video url (?=xxxxxxxxx to ?=xxxxxxxxx.) and press enter, it gets removed -- actually triggering a 303 HTTP response).
In general, you should use only upper/lowercase letters and non-trailing dashes or dots in urls to compose the so-called slug, which is guaranteed to be handled correctly across all decent browsers and web servers.

RewriteCond Simple Issue

I need the URl http://mydomain.com/careers to go to http://mydomain.com/#!/careers
For what it's worth I've tried numerours variations around this with no success
RewriteCond %{REQUEST_URI} !^/careers$
RewriteRule (.*) /#!/careers [QSA,L]
Can anyone help?
"!" in your RewriteCond line means "not"...
Also, "[QSA,L]" means:
L means this is last rule (processing terminates after matching this one) and
QSA means query string append
But, becase R flag was not specified, this is done by sub-request and not a redirect, so the actual URL in your browser does not change...
Try this:
RewriteEngine on
RewriteCond %{REQUEST_URI} ^/careers$
RewriteRule (.*) /#!/careers [R,L,QSA]
Hope this helps

mod_rewrite: what does this RewriteRule do?

Given these conditions (I know what they mean/do):
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
What does the first rule do? What is that lonely dash for?
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]
I've been using this for quite some time now in combination with the Zend Framework, but I never really got what the first rule does exactly.
The RewriteCond directive just describes an additional condition for a RewriteRule directive. So RewriteCond must always be associated with a RewriteRule.
In your case the three RewriteCond probably belong to the first RewriteRule like this:
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
Now this rule is applied if the pattern of the RewriteRule matches the current request URL (per-directory path stripped before) and if the condition is fulfilled.
In this case the condition is only true if when mapping the request URL to the filesystem it matches either an existing file with the file size greater than 0 (-s), or a symbolic link (-l) or a directory (-d). So your rule will be applied for any URL (^.*$ matches anything) that can be mapped to something existing in your filesystem. The substitution - just means to not change anything. And the NC (no case, case insensitive, useless in this context) and L (last rule if applied) are flags that modify either the pattern, replacement or the execution of the rule.

mod_rewrite trailing slash with RewriteCond

I have searched through the related mod_rewrite qustions but I can't anything specific enough so I'll post:
Heres my rule that adds a trailing slash:
RewriteCond %{REQUEST_URI} ^/[^\.]+[^/]$
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]
Only I want to exclude one specific directory eg. /mydirectoryname/ and not add the trailing slash to anything that starts with that. Reason being its breaking some of my ajax calls.
Add another RewriteCond:
RewriteCond %{REQUEST_URI} !/mydicrectoryname)/
You can descibe that with just one condition:
RewriteCond $0 !^mydirectoryname(/|$)
RewriteRule ^[^\.]+[^/]$ /$0/ [R=301,L]

Resources