I've tried searching but can't find a solution that works.
I'd like to remove the .php extension and add a trailing slash from a specific URL (about.php).
For instance...
www.example.com/about.php should redirect to www.example.com/about/
and
www.example.com/about should redirect to www.example.com/about/
I've tried various RewriteRules in .htaccess but none of them worked the way I needed them to.
The following adds the trailing slash, and I can access the page without the extention... but the .php version still shows up.
RewriteEngine On
RewriteBase /
RewriteRule ^about/$ about.php [END,QSA,NC]
Does anyone have any idea how to get this working?
To externally redirect /about.php (and /about - no trailing slash) to /about/ (for the benefit of search engines and external third parties that may have indexed/linked to the old URL) you would need to add something like the following before your existing rewrite:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^about(\.php)?$ /about/ [R=302,L]
The condition that checks against the REDIRECT_STATUS environment variable is to ensure we only redirect direct requests and not already rewritten requests by the later rewrite. REDIRECT_STATUS is empty on the initial request and gets set to the HTTP status (ie. "200" on success) after the first rewrite.
The RewriteRule pattern matches both /about and /about.php and redirects to /about/.
Note that this is a 302 (temporary) redirect. Only change it to a 301 (permanent) - if that is the intention - once you have confirmed it works OK. This is to avoid caching issues (301s are cached persistently by the browser). Likewise, if you have previously experimented with 301s then you will need to make sure the browser is cleared before testing.
UPDATE#1: If I wanted to do this to multiple pages
If you have just two pages (as in your example/comment) then you can combine both rules into one (rather than duplicating the rule block). For example:
RewriteEngine On
RewriteBase /
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(about|contact)(\.php)?$ /$1/ [R=302,L]
RewriteRule ^(about|contact)/$ $1.php [END,QSA,NC]
The $1 backreference will hold either "about" or "contact" from the capturing group in the RewriteRule pattern.
UPDATE#2: ... I'm starting to add a lot of pages and I'm afraid the list will get out of control. Is there a better way to handle tons of pages... like 50+?
If all your pages are files in the document root then you could generalise the regex and essentially rewrite everything. For example:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^([\w-]+)(\.php)?$ /$1/ [R=302,L]
RewriteRule ^([\w-]+)/$ $1.php [END,QSA,NC]
This is no longer "specific", as it redirects/rewrites any URL that simply "looks like" a valid request.
\w is the shorthand character class for any word character (ie. a-z, A-Z, 0-9 and _), to this we add the hyphen. So your URLs/filenames can only consist of those characters.
The above will match /about/, /contact/, /something-else/ and /foo_BAR_123/, etc. But it won't match /foo.jpg, /foo/about/ or /foo.bar/, etc.
If required, you could first check that the file exists before rewriting to it (or redirecting), but that is relatively expensive and probably not required here.
You can use index.php instead.
index page does not show up on browser address.
You can create http://www.example.com/about/index.php and on the browser, it will show as:
http://www.example.com/about/
If you need to redirect from http://www.example.com/about.php I can recommend use header http-equiv:
< meta http-equiv="refresh" content="0; URL='http://www.example.com/about/'" />
Related
I have run into an issue with my .htaccess file.
The file changes the ugly URL such as http://localhost/news.php?article_slug=example-1 to http://localhost/news/example-1
This works perfectly, but when I go to http://localhost/news i get a 404 error.
Within news.php I have a redirect; so if there is not an article slug in the URL it will redirect to latest.php.
my PHP code on news.php
$article_slug=$_GET['article_slug'];
if (empty($_GET)) {
header("Location: ../latest.php");
die();// no data passed by get
}
This is what I currently have in my .htaccsess file
Options -MultiViews
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
RewriteRule ^news/([\w\d-]+)$ /news.php?article_slug=$1[QSA,L]
RewriteCond %{QUERY_STRING} article_slug=([\w\d-]+)
RewriteRule ^news$ /news/%1 [R,L]
RewriteRule ^category/([\w-]+)$ /category.php?category_slug=$1&page=$2 [QSA]
When I try to debug this myself (with very little knowledge) and add the following line it redirects to latest.php
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
but on the redirected page I get the following error
The page isn’t redirecting properly
Firefox has detected that the server is redirecting the request for this
address in a way that will never complete.
This problem can sometimes be caused by disabling or refusing to accept cookies
When I use the developer tools in firefox as IMSoP commented all I see is latest.php reloaded multiple times.
This is not just isolated to just latest.php but any file on the server thats not listed in the access file
when I remove the line
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
I can load the PHP file but it doesn't redirect from news.php and http://localhost/news is not found but http://localhost/news/example-1 works.
but when I go to http://localhost/news i get a 404 error.
None of your rules catch such a request, so no rewriting occurs and you get a 404.
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
This rewrites everything to /news.php, including /latest.php that you are redirecting to in your PHP script and the cycle repeats, resulting in a redirect loop to /latest.php.
However, this redirect in your PHP code would also seem to assume there is a slash on the original request. ie. should be /news/ (with trailing slash) not /news (no trailing slash) as you state in the question.
It would be better to redirect to a root-relative (or absolute URL) in your PHP script. ie. header('Location: /latest.php');
RewriteRule ^news/([\w\d-]+)$ /news.php?article_slug=$1[QSA,L]
RewriteCond %{QUERY_STRING} article_slug=([\w\d-]+)
RewriteRule ^news$ /news/%1 [R,L]
(Note you are missing a space before the "flags" argument in the first rule.)
The first rule can be modified to allow news/ and not just news/<something>. This is achieved by simply changing the quantifier from + (1 or more) to * (0 or more) on the capturing subpattern.
The second rule is not currently doing anything. You should probably be targeting news.php here. But the rules are also in the wrong order.
As noted in my answer to your earlier question, the \d shorthand character class is not necessary, since \w (word characters) already includes digits.
Try the following instead:
RewriteCond %{QUERY_STRING} (?:^|&)article_slug=([\w-]*)(?:$|&)
RewriteRule ^news\.php$ /news/%1 [R=301,L]
RewriteRule ^news/([\w-]*)$ /news.php?article_slug=$1 [QSA,L]
Note, the first rule should be a 301 (permanent) redirect, not a 302 (as it was initially). But always test first with a 302 to avoid potential cachining issues.
This allows requests to /news/, but not /news (no trailing slash). Only one of these can be canonical. If you need to handle /news as well then you should redirect to append the trailing slash (so /news/ is canonical, and the URL you should always link to.) For example, before the above two rules:
# Append trailing slash if omitted (canonical redirect)
RewriteRule ^news$ /$0/ [R=301,L]
Summary
Bringing the above points together:
Options -MultiViews
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# Append trailing slash if omitted (canonical redirect)
RewriteRule ^news$ /$0/ [R=301,L]
RewriteCond %{QUERY_STRING} (?:^|&)article_slug=([\w-]*)(?:$|&)
RewriteRule ^news\.php$ /news/%1 [R=301,L]
RewriteRule ^news/([\w-]*)$ /news.php?article_slug=$1 [QSA,L]
RewriteRule ^category/([\w-]+)$ /category.php?category_slug=$1 [QSA,L]
However, the same now applies to your /category URL. This was also discussed in your earlier question. (I've removed the superfluous &page=$2 part and added the missing L flag.)
If you have many such URLs that follow a similar pattern (eg. news and category etc.) you don't necessarily need a separate rule for each. (An exercise for the reader.)
UPDATE:
$article_slug=$_GET['article_slug'];
if (empty($_GET)) {
header("Location: ../latest.php");
die();// no data passed by get
}
As discussed in comments, this should read:
$article_slug = $_GET['article_slug'] ?? null;
if (empty($article_slug)) {
header("Location: /latest.php");
die(); // no data passed by get
}
Rewrite rules are at heart very simple: they match the requested URL against a pattern, and then define what to do if it matches.
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
The pattern here translates as "must match right from the start; anything other than a slash, zero or more times; optional slash; must match right to the end". The action if it matches is to act as though the request was for "/news.php", adding on any query string parameters.
That's a very broad pattern; it will match "news" and "news/" but it will also match "hello-world", and "__foo--bar..baz/". The only thing that would stop it matching is other rules higher up your config file.
Meanwhile, every time this rule matches, your PHP code in news.php will run, and if there isn't anything on the query string, will tell the browser to request "latest.php".
But the rule will also match "latest.php". So when the browser requests "latest.php", the code in "news.php" gets run, and tells the browser to request "latest.php" again ... and we have an infinite loop.
The simplest fix is just to make your rule more specific, e.g. look specifically for the word "news":
RewriteRule ^news/?$ /news.php [L,QSA]
Another common technique is to add a condition to the rule that it only matches if the URL doesn't match a real filename, like this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
Using .htaccess, I'm trying to find out how to redirect any requests to my site base URL / domain but to allow any requests with query strings, and if possible, only certain prepended query strings. For example:
Request to / is redirected to blog.example.com/
Request to /?anything (even without =) will be served as usual
Bonus points if anyone can describe how all pages would be redirected unless only containing certain query strings.
You can use this rewrite rule:
RewriteEngine on
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^/?$ https://blog.example.com/ [R=301,L]
The condition with %{QUERY_STRING} ^$ ensures that the query string is empty (starts with symbol followed by ends with symbol.)
The rule ensures that the path contains at most a single slash and causes the redirect.
If you want to redirect everything except a specific query string, you can use a very similar rule. This one will redirect any query string on the root path except /?foo=bar
RewriteEngine on
RewriteCond %{QUERY_STRING} !^foo=bar$
RewriteRule ^/?$ https://blog.example.com/ [R=301,L]
No matter how many rewrite rules you have, you only need one instance of RewriteEngine on in your .htaccess.
I have added extra rules in my .htaccess file.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule !^/articles/(.*)$ https://%{HTTP_HOST}/index.php?name=$1 [L,R=301]
RewriteRule ^(articles/)(.*)$ https://%{HTTP_HOST}/index.php?name=$2 [L,R=301]
</IfModule>
I have two conditions which seems to work:
http://example.com/this-is-old-url/ meets the first rewrite rule
http://example.com/articles/this-is-old-url/ meets the second rule
But the problem is that after these rules are added my website is starting to make redirects everywhere. For example, I can't access my domain https://example.com which changes to https://example.com/index.php?name=. All my website links breaks.
What am I doing wrong?
RewriteCond %{HTTPS} off
RewriteRule !^/articles/(.*)$ https://%{HTTP_HOST}/index.php?name=$1 [L,R=301]
RewriteRule ^(articles/)(.*)$ https://%{HTTP_HOST}/index.php?name=$2 [L,R=301]
The first rule matches everything, since the URL-path that is matched by the RewriteRule pattern in an .htaccess context never starts with a slash (you appear to have recognised this in your second rule). You also can't have capturing groups in negated patterns - by definition, a negated pattern doesn't actually match anything.
And the second rule executes unconditionally, for both HTTP and HTTPS, since RewriteCond directives only apply to the first RewriteRule that follows. (But these two rules can be combined into one anyway.)
To externally "redirect" (as per your example) a URL of the form http://example.com/this-is-old-url/ (or http://example.com/articles/this-is-old-url/) to https://example.com/index.php?name=this-is-old-url then you can do the following at the top of your .htaccess file:
RewriteCond %{HTTPS} off
RewriteRule ^(?:articles/)?([\w-]+/)$ https://%{HTTP_HOST}/index.php?name=$1 [R=301,L]
UPDATE: the regex now includes - (hyphens) and trailing slash. Note that the trailing slash is included as part of the capturing group. So, it redirects to /index.php?name=this-is-old-url/ (with the trailing slash).
The subpattern (?:articles/)? is optional and non-capturing. So, this one rule matches both your example URLs.
The <IfModule> wrapper is not required. And neither is the RewriteEngine directive, since you presumably already have that in the WordPress code block that follows.
You don't necessarily need to expose the index.php, you could redirect to /?name=... instead, assuming the DirectoryIndex is set correctly.
You will need to clear your browser cache before testing. (Preferably test first with 302 - temporary - redirects.)
I can't access my domain https://example.com which changes to https://example.com/index.php?name=.
That couldn't have happened with your existing rules, only if you'd requested http://example.com (note HTTP, not HTTPS).
So I am trying to clean up my search engine redirect errors. I had some old development sites that got indexed that I want to redirect to the main site.
Basically I want everything in the /dev/ folder to go to https://myfakewebsite.com/
I added this to .htaccess file in the /dev/ directory:
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^(.*)$ /$1 [R=301,L]
I tried this but it doesn't quite work, it does take out the /dev/ but it keeps the rest of the link there.
For example: https://myfakewebsite.com/dev/index.php?route=product/category&path=122
redirects to: https://myfakewebsite.com/index.php?route=product/category&path=122
I want it to redirect to: https://myfakewebsite.com/ (removing the /index.php?route=product/category&path=122 part).
Is there a way to do this with .htaccess?
You can use the following rule in your /dev/.htaccess
RewriteEngine on
RewriteRule (.*) / [L,R=301]
The rule above redirects all requests from /dev to the root of site ie . /dev/* => / including query strings from /dev?querystring to /?querystring . By default mod-rewrite appends old query string to the destination path so if you do not want it then you can use QSD flag in your rule or you can another rule to handle and remove QUERY_STRING .
You can use the following rules to redirect urls with and without query string to / . The ? at the end of the rule's destination discards the old query string.
RewriteEngine on
# redirect /dev?querystring to /
RewriteCond %{QUERY_STRING} .+
RewriteRule (.*) /? [L,R=301]
# redirect /dev/uri to /
RewriteRule (.*) / [L,R=301]
Make sure to clear your browser cache before testing these redirects.
You might want to achieve this kind of "trivial" redirect with the RedirectMatch directive from mod_alias, instead of doing this with mod_rewrite.
RedirectMatch 301 "/dev/(.*)" "/$1"
See documentation at https://httpd.apache.org/docs/2.4/mod/mod_alias.html.
You left the match results as part of the redirect on this line:
RewriteRule ^(.*)$ /$1 [R=301,L]
Specifically $1 as part of /$1 (the redirecting portion of the rule). Since this file is within the /dev directory it automatically removes /dev/ then applies the match to the redirect. Since you are using the capture ($1) as part of the rule, it'll take everything it matched after /dev/ (in your rule you should read it as ^/dev/(.*)$) and apply it to /$1, where $1 is whatever it "found" during ^(.*)$ in the previous step.
Try removing the pattern match, e.g.:
RewriteRule ^(.*)$ / [R=301,L]
Additionally, since you aren't using the results of the match you can remove the parenthesis (e.g. ^.*$). You can also put this .htaccess file in the root and simply redirect ^/dev(/?.*)$ to / depending on how you want to manage your subdirectories.
I know how to make URL's rewrite, for example:
www.example.com/index.php?id=1&cat=3 to www.example.com/1/3/ (or whatever). I know that.
What I don't know is how on earth to change my whole links in all pages to link to pretty URL's. All my site's links are old fashion (<a href="index.php?id=1&cat=2">) and there are many.
I`m asking if someone has an idea or know how to automaticaly redirect to that pretty url if the user click on index.php?id=1. (Almost like this site Stackoverflow if you change title in the url).
So my presumtions are...
Use .htaccess to read the index.php?id=1&cat=2 to rewrite index/1/3 that itself interprets again (strange)
a php file to do the redirects that htaccess rewrites back to original...
Conclusion: change <a href="index.php?id=1&....."> automaticaly to index/1/2
SOLVED
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
##################################
# This turns index.php?id=1&cat=2 into index/1/2 and then back 'transparent' into index.php?id=1&cat=2 if you have old fashioned
# links in your site and don't want to change them :)
# Avoid mod_rewrite infinite loops
# This is critical if you want to use this code
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule .* - [L]
# Hard-rewrite ("[R]") to "friendly" URL.
# Needs RewriteCond to match original querystring.
# Uses "?" in target to remove original querystring,
# and "%n" backrefs to move its components.
# Target must be a full path as it's a hard-rewrite.
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$
RewriteRule ^index.php$ http://localhost/index/%1/%2/? [L,R]
# Soft-rewrite from "friendly" URL to "real" URL.
# Transparent to browser.
# Won't re-trigger the above rewrite, though I'm
# not really sure why! The order of the rules
# doesn't seem to make a difference.
RewriteRule ^index/(\d+)/(\d+)/$ index.php?id=$1&cat=$2 [L]
RewriteEngine on
# Prevents browser looping, which does seem
# to occur in some specific scenarios. Can't
# explain the mechanics of this problem in
# detail, but there we go.
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule .* - [L]
# Hard-rewrite ("[R]") to "friendly" URL.
# Needs RewriteCond to match original querystring.
# Uses "?" in target to remove original querystring,
# and "%n" backrefs to move its components.
# Target must be a full path as it's a hard-rewrite.
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$
RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R]
# Soft-rewrite from "friendly" URL to "real" URL.
# Transparent to browser.
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2
Of course, ideally, you'd just fix your links, and then you'd only require the soft-rewrite. :)
Tested with Apache/2.2.3. I think I made up the terms "hard-rewrite" and "soft-rewrite".
Why not just change your index.php file to do it? You could theoretically do a little more error checking that way, allowing for the variables to be in any order and still get routed to the correct end location.
<?php
// Permanent redirection
header("HTTP/1.1 301 Moved Permanently");
header("Location: http://www.example.com/{$_GET['id']}/{$_GET['cat']}");
I didn't do any error checking here, but wanted to give a base example.
On second thought I guess this is adding in functionality to the index.php file which you then want to use for your application itself, so perhaps it would end up confusing the functionality in the code.