URL rewrite conflicting rules - .htaccess

I'm having problems getting my URL rewrite to work, I currently have these 3 rules
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
and I want to add a rule where if the user hits http://example.com/metrics/, it sends them to index.php?/sign-in
I've tried doing RewriteRule /metrics/(.*) /index.php?/sign-in [PT], but it seems to be conflicting with the .* rule and not sending the user to /index.php?/sign-in at all.

If you have logically conflicting rules, put the more specific before the more general.
For your purpose, something like this should work:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^metrics/?(.*)$ /index.php?/sign-in [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
As an aside, you may want to consider revising your approach.
What if the user is already signed in? Perhaps, your /metrics page should behave (.htaccess wise) the same as your other pages, and have its code do the equivalent of if( !SignedIn() ) Redirect( "/sign-in" ) in your language of choice.

Related

rewrite rule for an URL with 2 queries

My URL looks like this:
example.com/index.php?f=directory&s=page
I want it to be like this instead:
example.com/directory/page
my rewrite rules look like this right now:
RewriteRule ^([A-Za-z0-9]+).html$ https://www.example.net/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?(.*?)/?$ index.php?s=$1 [L]
it appears to work but not quite because if I call the queries in PHP I get something like this:
print $_GET['f'] */ it prints nothing
print $_GET['s'] */ it prints directory/page
which is not what I intend. How can I fix it?
Thank you.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?(.*?)/?$ index.php?s=$1 [L]
Do it like this instead:
RewriteRule ^([^/]+)/([^/]+)$ index.php?f=$1&s=$2 [L]
Request /directory/page and it internally rewrites the request to index.php?f=directory&s=page.
By making the regex more specific, the filesystem checks can probably be avoided.
To redirect from index.php?f=directory&s=page to /directory/page (if these URLs have already been indexed and/or linked to by third parties), then add the following redirect before the above directive:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^f=([^&]+)&s=([^&]+)$
RewriteRule ^index\.php$ /%1/%2 [R=301,L]
But you must already be linking to the canonical /directory/page URL in your application.
Test with 302s to avoid potential caching issues. Clear your browser cache before testing.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?(.*?)/?$ index.php?s=$1 [L]
You are only assigning a single URL parameter s, so yes, $_GET['f'] will indeed be empty.

Wrapping legacy Application in Framework

I have a legacy-Application with bad code and very untested. It's a large one and so we don't have the manpower to develop a new version in one step.
So I'd like to wrap it into a Symfony application and write the new parts in Symfony. The old classes will then be refactored step by step.
I tried to include the legacys frontcontroller in the application if symfony fires a 404. That works well.
But if i use the app now, i have errors with it's old routing system and symfonys .htaccess.
The symfony .htaccess looks like this
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
And the legacy .htaccess looks like this
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !\.(css|jpg|png|gif|js|xml)$ [NC]
RewriteRule ^(.*)$ index.php?_shop_=$1 [L,QSA]
The _shop_ Parameter is used for routing etc. and the Router of the old application depends on it.
Any idea how i can geh this together?
If you want to fix this in .htaccess, you have to define the valid routes.
Let's assume your old applications has the following possible routes and redirects:
/listing => index.php?shop=listing
/detail => index.php?shop=detail
/cart => index.php?shop=cart
Then you'll want your .htaccess to look like this
RewriteCond %{REQUEST_FILENAME} (listing|detail|cart)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !\.(css|jpg|png|gif|js|xml)$ [NC]
RewriteRule ^(.*)$ index.php?_shop_=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
If this line is too simple for your needs, you can leverage the fact that in rewrite rules the OR flag has an higher precedence, and write something like:
RewriteCond %{REQUEST_FILENAME} ^listing/something$ [OR]
RewriteCond %{REQUEST_FILENAME} ^detail/[complexRegex] [OR]
RewriteCond %{REQUEST_FILENAME} ^cart/...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !\.(css|jpg|png|gif|js|xml)$ [NC]
RewriteRule ^(.*)$ index.php?_shop_=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
If you want something even more advanced, you'll have to tackle it at the application level (i.e. within index.php or app.php) and not in .htaccess

Need to rewrite parameters AND redirect with htaccess

I need to simultaneously do two things with htaccess.
I need to take a URL like:
http://client.example.com/123
and rewrite the directory to a param, and simultaneously add another subdomain to the url so it looks like this:
http://client.qa.example.com/?param=123
This does the param bit correctly, but I can't figure out how to add the subdir:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/[^/]+/?$
RewriteRule ^([^/]*)/?$ /?param=$1 [L]
You can examine the host header using a RewriteCond and extract the relevant parts of the name. Use them in the rewrite. Back references to matches in RewriteConds appear as %n
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_HOST} (.+?)\.(.*)
RewriteRule ^([^/]*)/?$ http://%1.qa.%2/?param=$1 [R,L]
(.+?)\.(.*) will do a match on everything up to the first . and then everything to the end. So client and example.com will respectively be in %1 and %2
If your .htaccess is in the root of client.example.com, it should be a simple redirect. Of course the directory has to be a fake directory or this won't redirect.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/?$ http://client.qa.example.com/?param=$1 [R=301,QSA,L]
You can use the following to match (check for htaccess syntax):
(http://[^.]+\.)([^/]+/)([^/]*)/?$
And replace with:
$1qa.$2?param=$3
See DEMO
Finally got it working using:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_HOST} (.+?)\.(.*)
RewriteRule ^([^/]*)/?$ http://%1.qa.%2/?param=$1 [R,L]
Now I just have to figure out how to work in 2 parameters, given that param 2 isn't always going to be present.

rewrite url to root directory with url cutting

I have such urls:
http://site.ru/ontent/wefwefw/article.php?article=369-tayskaya-kuhnya-recept-s-foto
http://site.ru/ontent/wefwefw/article.php?article=32237-ogurci-recepti-na-zimu
http://site.ru/ontent/wefwefw/article.php?article=90-ogurci-na-zimu-recepti-po-koreyski
I want to rewrite tham like:
http://site.ru/tayskaya-kuhnya-recept-s-foto.html
http://site.ru/ogurci-recepti-na-zimu.html
I tried smth like:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ ontent/wefwefw/$1 [L]
But how cut unnecessary parts of string?
Unless article.php is able to derive the right article from the title alone, what you want to do is not possible. mod_rewrite is good at rewriting things, but it can't summon an article-id from thin air if it isn't in the original request. You would have something like:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+)\.html$ ontent/wefwefw/article.php?article=$1 [L]
When you would request http://site.ru/tayskaya-kuhnya-recept-s-foto.html, it will load http://site.ru/ontent/wefwefw/article.php?article=tayskaya-kuhnya-recept-s-foto. Then you have to get the id 369 in some other way based on the title if needed.
The best in this case is:
http://site.ru/369-tayskaya-kuhnya-recept-s-foto.html
redirect to
http://site.ru/ontent/wefwefw/article.php?article=369
with:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(\d+)-[^/]*\.html$ ontent/wefwefw/article.php?article=$1 [L]

shorten/compress mod_rewrite rules

amongst my rules i have..
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2&info=$3&id=$4 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2&info=$3 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/?$ index.php?page=$1 [QSA,L]
Which seems a little bulky to me and repeats a lot of stuff, any ideas on how i can shorten it?
Can i set "global" conditions?
Can i have one rule like this that captures all the ones below it (i dont care if it sends through empty varibles)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule **clever stuff here** index.php?page=$1&submenu=$2&info=$3&id=$4 [QSA,L]
So it would capture
domain.com/a/b/c/d
and
domain.com/a/b/c
and
domain.com/a/b
and
domain.com/a
Answers, Thoughts, Musings, and Existential Arguments all appreciated...
You can pretty easily remove the conditions if you check for the inverse and let it pass through the rewrite engine. Then anything afterwards you know isn't an existing file or directory:
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2&info=$3&id=$4 [QSA,L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2&info=$3 [QSA,L]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ index.php?page=$1&submenu=$2 [QSA,L]
RewriteRule ^([^/\.]+)/?$ index.php?page=$1 [QSA,L]
It's not a fancy multiple-look-ahead regex that does it all in one line, but it does make it easier to read.
EDIT:
Well, like I said, you do it with multiple look-aheads, something like:
RewriteRule ^(?:([^/.]+)|)(?:/([^/.]+)|)(?:/([^/.]+)|)(?:/([^/.]+)|) index.php?page=$1&submenu=$2&info=$3&id=$4 [QSA,L]
But it's harder to read, and more likely to do something unexpected if you change something. Additionally, whereas having explicit 4 different mappings, you don't get blank query string params like you do with look-aheads. Whether the last (?:/([^/.]+)|) is there or not, you will still get an id param (just blank).

Resources