Rewrite rule only if other rules do not apply - .htaccess

I have the following rules:
RewriteRule ^error$ 404.php
RewriteRule ^home$ index.php
Now I want, that everything behind domain.com<> (in the <>) redirects to test.php.
I did
RewriteRule ^([^/]+)$ test.php
and that works but when I enter "error" or "home" it also redirects to test.php instead of to 404.php and index.php

You can REDIRECT_STATUS like this:
RewriteEngine On
RewriteBase /
RewriteRule ^error/?$ 404.php [L,NC]
RewriteRule ^home/?$ index.php [L,NC]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^ test.php
REDIRECT_STATUS gets set to non-zero value (200) after execution of some rewrite rules.

Try this :
RewriteEngine On
RewriteRule ^error$ 404.php
RewriteRule ^home$ index.php
RewriteCond %{REQUEST_URI} !/(error|home|index|404)
RewriteRule ^([^/]+)$ test.php
The problem is that , you have already captured error and home but when they redirected to 404 or index they being captured again by the last rule so you should exclude them from last rule.

You can tell the rewrite that it has completed when a matching condition is found and therefore not to continue when the condition is met so your rewrites could be something like this:
RewriteEngine On
RewriteBase /
RewriteRule ^error$ 404.php [L,NC]
RewriteRule ^home$ index.php [L,NC]
RewriteRule ^(.*)$ test.php
The 'L' rewrite flag tells the redirect that this is the last condition (i.e. look no further). The 'NC' flag make the rule case insensitive. You can decide if this you also want to include need a R=301 flag or not.
However, I can see a potential issue in the above because once the rules have been processed, the rewritten request is handed back to the URL parsing engine and the rewritten request is handled again by the .htaccess file. This will cause the second last rule to be accepted on the second pass.
The alternative [END] flag, can be used to terminate not only the current round of rewrite processing but prevent any subsequent rewrite processing from occurring in the htaccess context. I will be interested if this works.
Hope this helps.

Related

Apache redirect directive not acting as final rule

I am trying to achieve a simple redirect - from /news to /insights
I have the following in my .htaccess file:
redirect 301 /news /insights
<IfModule mod_rewrite.c>
RewriteEngine On
# Send would-be 404 requests to Craft
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
RewriteRule (.+) index.php?p=$1 [QSA,L]
</IfModule>
Whenever I've used the redirect directive before, any matched URLs would be redirected and no further rewrites in the file would be processed. That is to say, going to /news would send you to /insights, and the rewrite to index.php would not be processed.
However, with this current setup, going to /news sends me to /insights?p=news, so for some reason the rewrite to index.php is still being processed.
Furthermore, if I comment out the index.php rewrite, then I get sent to /insights as expected.
This isn't how I've usually experienced this working so am unsure why it's doing this.
I have also tried the following:
RewriteEngine On
RewriteRule "^/news" "/insights" [R=301,L]
This simply results in a 404 instead of redirecting, which I also do not understand.
I am aware I could do the following:
RewriteEngine On
RewriteCond %{REQUEST_URI} "^/news"
RewriteRule ^ /insights [R=301,L]
which does work, however, I don't really want to have multi-line rewrites for lots of URLs, and would like to understand why the other 2 examples do not work.
You just need to insert this rule before last catch-all rule.
RewriteRule ^/?news/?$ /insights [R=301,L,NC]
Place it just below RewriteEngine On line so that mod_rewrite engine executed this rule before other rule.
Make sure to test it in a new browser.

Exact match specific word with RewriteRule in .htaccess

I'm setting up my .htaccess files with some RewriteRules, but unfortunatly I got stuck. I've found a lot of topics on this, but none mentioning my specific issue.
I have this:
RewriteEngine On
RewriteBase /
RewriteRule ^en/contact /contact.php?lang=en&slug=ok [L,NC]
RewriteRule ^contact /en/contact [L,R=301]
Purpose is to force the lang as a first parameter in the url if not set.
So: www.example.com/contact should be redirected (301) to www.example.com/en/contact which then again loads the content from /contact.php?lang=en&slug=ok
The problem I have is that I end up in a loop as /contact.php?lang=en&slug=ok gets loaded and is parsed by RewriteRule ^contact /en/contact [L,R=301] again.
How can I adapt this rule so it is only triggered for /contact and not for /contact.php or /en/contact or /contactme or any other url that contains contact
Thanks!
Your 301 redirect rule is causing the infinite loop as it matches the destination path of your first rule. What is happening is that when you request /en/contact your first rule rewrites it to /contact.php?lang=en&slug=ok and then ,your second rule matches the same uri and redirects it to /en/contact .
To fix this, You can either use END flag in your RewriteRule or use THE_REQUEST condition .
RewriteEngine On
RewriteBase /
RewriteRule ^en/contact /contact.php?lang=en&slug=ok [L,NC,END]
RewriteRule ^contact /en/contact [L,R=301]
Note : END flag works only on apache versions above 2.4 if your server version is less then 2.4 you could use a % {THE_REQUEST} condition to terminate the redirect loop
RewriteEngine On
RewriteBase /
RewriteRule ^en/contact /contact.php?lang=en&slug=ok [L,NC]
RewriteCond %{THE_REQUEST} /contact
RewriteRule ^contact /en/contact [L,R=301]
Make sure to clear your browser cache before testing these redirects.

(htaccess) Rewrite English names of php files to their Dutch equivalents

I would like to rewrite the English names of php files to their Dutch equivalents.
For example: someurl.com/news.php?readmore=4#comments should become someurl.com/nieuws.php?leesmeer=4#kommentaar. The code from news.php should be executed but nieuws.php should be in the url the arguments should function as well.
I tried several htaccess examples but I can't get it to work.
Any help would be appreciated.
Edit: Working progress from answers below and final solution.
RewriteCond %{QUERY_STRING} ^readmore=(.*)$
RewriteRule ^news.php$ nieuws.php?leesmeer=%1 [R=301,L]
RewriteCond %{QUERY_STRING} !^norewrite[\w\W]*$
RewriteRule ^news.php$ nieuws.php [R=301,L]
RewriteRule ^nieuws.php$ news.php?norewrite [QSA]
RewriteCond %{QUERY_STRING} !^norewrite[\w\W]*$
RewriteRule ^search.php$ zoeken.php [R=301,L]
RewriteRule ^zoeken.php$ search.php?norewrite [QSA]
# make sure rewrite is activ
RewriteEngine On
# Rewrite a request for nieuws.php to news.php
RewriteRule ^nieuws.php$ news.php
Should do the trick.
Instad you could send all requests to an index.php and parse them there:
## Redirect everything to http://hostname/?path=requested/path
RewriteEngine On
RewriteRule ^([\w\W]*)$ index.php?path=$1 [QSA]
[QSA] makes sure you get the original get arguments too.
Now you have to parse the request in $_GET['path'] in you index.php and include the requested page.
eg:
if ($_GET['path'] == 'nieuws.php') {
include 'news.php';
} else if (empty($_GET['path'])) {
echo "HOME";
}
if you want to make make the user always sees nieuws.php in its address bar, even if he requested news.php, you could try the following:
RewriteEngine On
# Redirect news.php to nieuws.php if and only if the request comes from the client
# (suppose the client didn't set ?norewrite.)
RewriteCond %{QUERY_STRING} !^norewrite[\w\W]*$
RewriteRule ^news.php$ nieuws.php [R=301,L]
# Send news.php if nieuws.php was requested and prevent news.php from being redirected
# to back to nieuws.php by the rule above.
RewriteRule ^nieuws.php$ news.php?norewrite [L,QSA]
(R=301 means send a "moved permanently" redirect to the client, L means stop rewriting after this rule matched)
The hole thing with norewrite (you could use something else instead) is only needed to avoid an endles loop of rewriting between news and nieuws.
To translate the GET arguments, you can try the following code before the first line of the above code:
RewriteCond %{QUERY_STRING} ^readmore=(.*)$
RewriteRule ^news.php$ nieuws.php?lesseer=%1 [R=301,L]
Things after a the # in an url can't be changed in .htaccess, since they aren't send to the server at all. The only chance to change them is using JavaScript. (See lots of question here on manipulating them within JavaScript)
Try:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /news\.php
RewriteRule ^ /nieuws.php [L,R=301,QSA]
RewriteRule ^nieuws\.php$ /news.php [L,QSA]

SEO Url for Profiles

Preface
I'm trying to re-write a URL for a profile page. All of my application pages have a .html extension, so I'm trying to match just letters, numbers, -, and ..
So these would be valid
site.com/steve
site.com/steve-robbins
site.com/steve.robbins
But these wouldn't be
site.com/steve.html
site.com/steve-robbins.php
Assume I have a check in place so that custom URLs don't have .html or .php on the end.
Problem
I'm currently using this but it's not working
RewriteRule ^([a-zA-Z0-9\.-]+)$ profile.php?url=$1 [L]
It should set url to steve, but it's setting it to profile.php
What am I doing wrong?
My complete .htaccess
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^[^.]+\.[^.]+$
RewriteRule ^(.*) http://www.%{HTTP_HOST}/$1 [R=301]
#
# LOGIN
#
RewriteRule ^([a-z0-9]{255})/activate\.html$ login.php?activate=$1 [L]
RewriteRule ^logout\.html$ login.php?logout [L]
#
# SETTINGS
#
RewriteRule ^change-([a-z]+)\.html$ account-settings.php?$1 [L]
RewriteRule ^([a-zA-Z0-9\.-]+)$ profile.php?url=$1 [L]
# SEO friendly URLs
RewriteRule ^([a-zA-Z0-9-_.]+)\.html$ $1.php [L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([a-zA-Z0-9-_.]+)\.php
RewriteRule ^([a-zA-Z0-9-_.]+)\.php$ $1.html [R=301]
Add this to the top of your rules (under the RewriteBase / directive):
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
That should stop it from looping. The rewrite engine will keep re-applying all the rules until the URI going in (sans query string) is the same as the URI that comes out of the rules. That's why the value of url is profile.php.
I'm kind of a beginner in interpreting mod_rewrite rules but if I understand it correctly your rule is matched and than matched again, either add something to the url matching scheme like /profile/user or add a condition to not redirect if already redirected
Try adding a leading slash to the redirect like this:
RewriteRule ^([a-zA-Z0-9.-]+)$ /profile.php?url=$1 [L]
The reason you're getting a url value of profile.php is because the [L] flag is kinda misleading when it comes to the .htaccess file. In the server config files it does exactly what you'd think, but in the .htaccess file it stops reading rules at that rule, but then goes through the rules again until path is unchanged by any of the rules. By adding the leading /, your rule will not match the second time around as you exclude / from the regex. I spent a while struggling with this feature myself.

How come the [L] flag isn't working in my .htaccess file?

Here are the rules:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ index.php?action=home [L]
RewriteRule ^[\w\W]*$ error.php [L]
When a page matches the first one, it is supposed to ignore any other further rules. Yet accessing / results in error.php being invoked. Commenting out the second rule works as intended - the page redirects to index.php.
What am I doing wrong?
Also: is there a better way to write the last line? It's basically a catch-all.
You could change
^[\w\W]*$ to ^[\w\W]+$ or ^.+$
If the last line is a catch-all, just do RewriteRule ^.*$ error.php [L]
Your first line might be erroring out when you send a request for / because your rule is saying "nothing" and you are sending /.
Try changing your rule to RewriteRule ^/$ index.php?action=home [L]

Resources