To remove trailing extension (a .php in this case), I have tried the following two set of rules
RewriteCond %{THE_REQUEST} (\S*?)/(.*?)\.php [NC]
RewriteRule ^ %1/%2 [L,R=301]
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteRule !\.php$ %1.php [NC,L]
and
RewriteRule ^(.*?)\.php$ $1 [NC,L,R=301]
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteRule !\.php$ %1.php [NC,L]
To convert a url like test.com/test.php to test.com/test, the first one works while the second one ends up looping infinitely. The rewrite_log is just confusing, so can anyone tell what is the problem with the second set?
PS: The rules might be inefficient or even not sufficient for a case like test.com but that's not the issue. I just want to understand how the rewrite is working BTS.
As mentioned in the comments, in the first case, there is no looping using %{THE_REQUEST} as the value is static and doesn't change once it is received by the server even after redirecting / "looping". So the rule will remove trailing .php and redirect. As the rewrite rule is substituting values from %{THE_REQUEST} in the rewritecond, after redirection, the value will not contain the trailing .php and thus proceed on to the second rewritecond and then finally redirect internally. After a second redirect, both the conditions will fail to match thus stopping the process
Whereas in the second case, the rewrite rule checks values from the uri which will remove .php and redirect. After redirection, the second rule will internally redirect back to the uri with php extension. Then finally in the supposed third final check which is supposed to not match both the rules, the first rule will again be matching thus infinitely looping the entire process.
Related
I have a problem with transforming urls from dynamic to static.
I have a site where different pages are generated with a dynamic url, like:
www.example.com/?pr=project-abc123
I would like to rewrite the url of each one with htaccess making it static, like this:
www.example.com/project-abc123
// or
www.example.com/pr/project-abc123
Now, i found this htaccess code that seems to work:
RewriteEngine On
RewriteBase /
RewriteCond %{THE_REQUEST} \?
RewriteCond %{QUERY_STRING} ^p=(.*)$
RewriteRule (.*) http://example.com/%1? [L,R=301]
RewriteRule ^(.*)$ /index/?pr=$1[L]
URLs are rewritten as indicated (first type, whitout /pr/ ), but gives me a multiple choice error. What am I doing wrong?
Let's understand RewriteRule directive which is the real rewriting workhorse.
A RewriteRule consists of three arguments separated by spaces. The arguments are
Pattern: which incoming URLs should be affected by the rule;
Substitution: where should the matching requests be sent;
[flags]: options affecting the rewritten request.
Let's start with the following snippet:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
First line of code, describes RewriteEngine is turned on. Here I use RewriteCond directive which defines a rewrite rule condition. Using RewriteCond directive we defined two conditions here. This directive took a server variable called REQUEST_FILENAME. The two conditions above tell if the request is not a file or a directory, then meet the rule set by RewriteRule. See more details on this issue.
Now it's time to write some rewrite rules. Let's convert
www.example.com/?pr=project-abc123
// to
www.example.com/project-abc123
and rewrite rule will be:
RewriteRule ^(.*)$ index.php/?pr=$1 [L]
And to get the www.example.com/pr/project-abc123 we need the rule as the below:
RewriteRule ^/?([a-z]+)/(.*)$ index.php/?$1=$2 [L]
// or
RewriteRule ^/?pr/(.*)$ index.php/?pr=$1 [L]
The [L] flag tells mod_rewrite to stop processing the rule set. This means that if the rule matches, no further rules will be processed.
Im having an issue with my joomla site. At first it would create duplicates of urls with and without trailing slash. So I implemented some htaccess code to force a trailing slash. Then it was causing soft 404s that is, if I entered a non existent page the status in my crawler was actually 200 for these non existing pages, but it was just a 404 page template. Not a hard 404. So I changed that in the "error.php" file.
However the issue is that when I enter any non-existent url like "domain.com/nonexistentpage" (without a trailing slash) it first redirects it via the 301 rule to the "domain.com/nonexistentpage/" and THEN returns a 404.
If I stop the redirecting of pages, than that same URL
"domain.com/nonexistentpage" will return a hard 404 as expected, but the URLs wont redirect to the trailing slash version (which I need). However if I redirect it, it will 301 before 404ing.
I dont have any pages on my site that are not without a trailing slash, so I want all pages that dont exist to go straight to my "error.php" file and be excepted from the 301 rule. How would I do this? Here is my coded attempt
RewriteCond %{REQUEST_URI} !-d
RewriteCond %{REQUEST_URI} !-f
RewriteRule .? - [S=1]
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ http://www.domainxyz.com/$1/
RewriteRule .+ - [L]
Im very inexperienced with htaccess. the idea was that if the file is non existent that the slash rewrite rule should be ignored.
If you look at it another way, I think what you're saying is:
If the REQUEST_FILENAME is a file or directory
and REQUEST_URI doesn't have a trailing slash
Then 301 to the same URL with the trailing slash...
Which translates to:
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !.*/$
RewriteRule ^(.*)$ /$1/ [R=301,L]
Because [OR] takes precedence over the implicit [AND] the rewrite conditions are basically read as (#1 OR #2) AND #3
Though actually your original method works with a small tweak:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .? - [S=1]
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]
Just ditch that final rewrite condition and change the rule slightly so that it only matches when there's not a trailing slash.
Still have not been able to solve this. I did notice one thing. If I add .html extension to each page then these redirect rules work. AKA the existent and non existent files are correctly separated by htaccess and rules applied accordingly. But if I leave them as is ending with a forward slash, it does not matter whether I add a condition first to check of the file or directory is real or not. They ALL redirect and THEN it 404s the nonexistent urls, but not 404 right away....
Cant understand what the issue could be.
I'm trying to set Semantic URLs via .htaccess (on local server). I've already replaced index.php?id=# with page# by the means of:
RewriteRule ^/?page([0-9]+)$ index.php?id=$1
It is working. That means that .htaccess is connected properly and runs. Now I want to "glue" the produced link with the original one. That is, to redirect from index.php?id=# on page#. Can't deal with it.
For example, I take only the page with id=0 and make this:
RewriteCond %{HTTP_HOST} ^site\.loc\/index\.php?id=0$ [NC]
RewriteRule ^(.*)$ http://site.loc/page0$1 [R=301,L]
Tell me, please, what is wrong with the rows above.
See also https://stackoverflow.com/a/31280108/345031 on "Ping-Pong" rewrites and how to enable the RewriteLog (or compare access/error.log) to debug such issues.
Your approach was on the right track. But I think for the old-to-new URL redirects you should cut it down to:
optional escape \?
↓ ↓
RewriteCond %{REQUEST_URI} ^/(?:index\.php)?\?id=(\d+)$
RewriteRule .* http://example.org/page%1 [R,END]
↑
Safer than [L] flag
The REQUEST_URI contains the ?-separated QUERY_STRING already. So the RewriteCond can match both. It's best to optionalize the index.php there, because that's not part of the incoming request URL.
(Capturing the page=id parameter per \d+ and %1 was already correct.)
Just use the [END] flag for both Ping-Pong rules. Make your existing internal/pretty-URL RewriteRule last. Otherwise they'd likely interact.
This should work :
RewriteEngine On
RewriteCond %{THE_REQUEST} /index\.php\?id=([^\s]+) [NC]
RewriteRule page%1? [NC,R,L]
RewriteRule ^/?page([0-9]+)/?$ index.php?id=$1 [NC,L]
I wanted to redirect/rewrite my name.php files to /name/
I found the solution on another topic (http://stackoverflow.com/questions/5527789/htaccess-rewrite-within-directory-hide-php-extension-and-force-trailing-slash)
Though, I wanted to learn it myself and started from scratch.
I first used this one, which makes eg .com/test/ show the content of .com/test.php:
RewriteEngine On
RewriteRule ^(.*)/$ $1.php
Then I tried the following, by itself, which redirects .com/test.php to .com/test/:
RewriteEngine On
RewriteRule ^(.*)\.php$ http://www.mydomain.info/$1/ [R=301]
So, both work on their own. But when I combine them, I get an loop error, even when I add [L] to it, which should mean the rules should only be used once. So this doesn't work:
RewriteEngine On
RewriteRule ^(.*)/$ $1.php [L]
RewriteRule ^(.*)\.php$ http://www.mydomain.info/$1/ [L,R=301]
I've probably made some stupid error but it seems logically to me...
Hope someone can point out my error.
Thanks.
Remove L-flag from first rule. That would stop "executing" and the second rule wouldn't be used. At the second rule you should keep the L flag, because it is the last one.
Because you have an external redirect with the R=301, adding L to it doesn't help as much as you need, as the redirect will come back to the server as a brand new request - where it again matches your first rewrite rule.
Instead, you need something like this:
RewriteCond %{THE_REQUEST} ^\w+\ /(.*)\.php\ HTTP/
RewriteRule ^ /%1/ [R=301]
RewriteRule ^(.*)/$ $1.php
Note that THE_REQUEST matches the entire line of the original request, e.g. GET /index.php HTTP/1.1. Even when %{REQUEST_URI} is rewritten to .php as part of the 2nd rule (where it will match on an internal sub-request), %{THE_REQUEST} is never rewritten, and this will ensure that the URL rewritten to .php doesn't match on the sub-request and result in another redirect sent back to the client.
I have the following in my .htaccess
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^task/(.*)/?$ index.php?mode=task&id=$1 [L]
RewriteRule ^(.*)/?$ index.php?mode=$1 [L]
If the last line is included, no matter what is put in the URL it sets $_GET['mode'] to index.php. Without the last line included, or if I set it to go to index.php?mode=home, for instance, it works fine, but there isn't a catchall.
I don't see what the problem is, but it's probably something simple. If someone else could take a moment to steer me right, that'd b great. Thanks!
When you apply that to ,say, /task/123, this is what happens, (assuming that URI doesn't exist):
passes !-f, /task/123 isn't a file that exists
passes !-d, /task/123 isn't a directory that exists
MATCH against ^task/(.*)/?$ so the URI gets rewritten to index.php?mode=task&id=123
With [L], nothing else happens and the request gets INTERNALLY REDIRECTED to index.php?mode=task&id=123
Internal redirect gets all rules re-applied <--- this is what's screwing you up
no match against ^task/(.*)/?$, do nothing
MATCH against ^(.*)/?$, so the URI gets rewritten to index.php?mode=index.php
initial URL equal rewritten URL: index.php, stop rewriting
What you need to do is add a condition to the 2nd rule:
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule ^(.*)/?$ index.php?mode=$1 [L]