Situation:
I have a few hundred posts each belonging to a particular category.
A] Now when the user visits the home page, the content is irrespective of the category sorted by date.
http://www.example.com
He can navigate through different pages like:
Type 1: http://www.example.com/3 which corresponds to http://www.example.com/index.php?page=3
I can probably do this in mod_rewrite
B] The user can then decide to view by category like:
Type 2: http://www.example.com/Football which will correspond to
http://www.example.com/index.php?page=1&category=Football
He can then navigate through pages like:
Type 3: http://www.example.com/Football/5 which =>
http://www.example.com/index.php?page=5&category=Football
C] I have a directory called View with index.php in it. It only shows individual posts like:
Type 4: http://www.example.com/View/1312 => http://www.example.com/View/index.php?id=1312
Here is the mod_rewrite I do:
RewriteEngine on
RewriteRule ^View/([^/.]+)/?$ View/index.php?id=$1 [L]
Now here are the problems I have
In point C]: http://www.example.com/View/1312 works fine but http://www.example.com/1312/
(notice the trailing slash) breaks apart & gives weird results.
Q1) So how do I maintain consistency here?
Q2) Ideally I would want http://www.example.com/View/1514 to show a 404 Error if there is no post with id 1514, but now I have to manually take care of that in PHP code.
What is the right way of dealing with such dynamic urls? especially if the url is wrong.
Q3) how do I ensure that http://www.example.com & http://www.example.com/ both redirect to http://www.example.com/index.php?page=1; (mod_rewrite code would be helpful)
Please Note that there are only two index.php files. One in the root directory which does everything apart from showing individual posts which is taken care by a index.php in View directory. Is this a logical way of developing a website?
Try these rules:
RewriteRule ^$ index.php?page=1 [L]
RewriteRule ^([0-9]+)/?$ index.php?id=$1 [L]
RewriteRule ^View/([0-9]+)/?$ View/index.php?id=$1 [L]
RewriteRule ^([A-Za-z]+)/?$ index.php?category=$1&page=1 [L]
RewriteRule ^([A-Za-z]+)/([0-9]+)/?$ index.php?category=$1&page=$2 [L]
As for the other question: Yes, since Apache can map these requests to existing files, it responds with a success status code. Now if your application decides that the requested resource does not exist, you need to handle that within your application and send an appropriate status code.
To fix the trailing slash, Just put /? before the $ at the end in your pattern
Related
Lately I have changed the way my website works - physical page for every article vs. dynamically loaded content without physical (sub)page, but I realized I cannot simply upload the new site files cos I would break up all the social platform sharing links, counters and stuff as there are literally thousands of the subpages.
I heard (I know about it) that via .htaccess and RewriteEngine (I need using RewriteEngine as all the code in htaccess is made for it) I can make pages load internally something completely different depending on the actual URL, like, for example, if I have actual URL link to one of my subpages:
http://sub.mypage.com/php/somearticle.php?j=en
...so without changing the text of the URL it would load my new site files internally on different principle, like this:
http://sub.mypage.com/?s=somearticle&j=en
Now I also need that those variables "j" and the "somearticle" to be dynamic, or better said they need to be copied exactly as they are from the physical URL in the addressbar (where "somearticle" is actually name of the originally physical php file and the "j" is just language variable) as it will be something else every time so I do not have to make thousands of lines in htaccess for every single concrete subpage - I need some universal code that would manage all the subpages (as the principle is the same for all, just php names changes and sometimes language = variable "j"), you see?
So can anyone help me telling me the exact syntax/code for this to achieve?
EDIT
So I was playing with it myself a bit and this seems to work if I set the subpage manually (NOTE: just a clarification - this is for my localhost:8081 therefore I have in place that 1st condition cos for server I have different version that has different path to index.php) + I slightly updated variable j part thanx to #Ben's post:
# LOAD PAGE DIFFERENT INTERNALLY - LOCALHOST
RewriteCond %{HTTP_HOST} ^localhost:8081 [NC]
RewriteCond %{REQUEST_URI} /php/(.*)\.php$ [NC]
RewriteRule ^php/(.*).php$ /WWW/_PHP_/lego/index.php?s=$1 [QSA,L]
But unfortunately for some reason it affects every page on my site not only pages under /php/, so when I click to go to my first (default/initial = index.php) page it breaks it (it holds summary of all articles - they are not loaded) - anyone knows why, please?
SOLVED
So after small change to #Ben's code this is the right solution thus I take his solution as the right one (as it would actually work OK right away as it is if the page would be on server cos my test version is on my localhost where the path to /php/ directory is different))
LOAD PAGE DIFFERENT INTERNALLY - localhost
RewriteCond %{HTTP_HOST} ^localhost:8081 [NC]
RewriteCond %{REQUEST_URI} /php/([a-zA-Z0-9\-]+).php [NC]
RewriteRule ^ index.php?s=%1 [QSA,L]
REQUEST_URI is path component of the requested URI such as /php/somearticle.php, but not contains query string such as ?j=en.
The RewriteCond pattern, ! character (exclamation mark) is used to negate the result of the condition. To prefix with some pattern, use ^ character (caret), matches the beginning of a line.
%1 is the RewriteCond backreference that provides access to the grouped parts (in parentheses) of the pattern.
QSA flag, if the replacement URI contains a query string, the query string such as ?j=en will be appended to the newly rewrite uri.
RewriteCond %{REQUEST_URI} ^/php/([a-zA-Z0-9\-]+).php [NC]
RewriteRule ^ index.php?s=%1 [QSA,L]
See also: Apache Module mod_rewrite, RewriteRule Flags
The requirement was to have:
http://xxxx.com/it/
To redirect to
http://xxxx.com/index.php?act=setlang&val=it
An the same t to happen for the rest of the links e.g
http://xxxx.com/it/test.php
to
http://xxxx.com/test.php?act=setlang&val=it
I have achieved that using the following:
RewriteRule ^([a-z]{2})$ index.php?act=setlang&val=$1 [L]
RewriteRule ^([a-z]{2})/([a-zA-Z0-9-]+)\.php$ $2.php?act=setlang&val=$1 [L]
The problem is that I would have like to have continuity of the language in the url path and retain it when changing links unless the lang var changes
When I visit http://xxxx.com/it (the language will be set to Italian as expected), when I click on the next link e.g test.php the link will be http://xxxx.com/test.php not http://xxxx.com/it/test.php.
Is there a way to retain it using htaccess until manually changed by the user (user selects another language)
The reason that I am looking to retain it is for SEO purposes really, not sure if it makes any difference? but i presume that if Google had to index 3 test.php (coming from it,en.fr) it wouldn't be able to crawl the individual languages at all?as well as if it has to crawl en/test.php fr/test.php and it/test.php..
Note: As you have guessed the sub-folders don't actually exist and in reality are virtual folders
To keep the language code sticky you can create a cookie first time then /it/ URL is loaded and then prefix every URI that doesn't have it.
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# if lang cookie is set and URI doesn't start with 2 char lang code
RewriteCond %{HTTP_COOKIE} LANG=([^;]+) [NC]
RewriteRule ^((?![a-z]{2}/).+)$ /%1/$1 [R,L,NC]
RewriteRule ^([a-z]{2})/?$ /index.php?act=setlang&val=$1 [L,CO=LANG:$1:%{HTTP_HOST}]
RewriteRule ^([a-z]{2})/([a-zA-Z0-9-]+)\.php$ $2.php?act=setlang&val=$1 [L,CO=lang:$1:%{HTTP_HOST}]
Is there a way to retain it using htaccess until manually changed by the user (user selects another language)
Htaccess can only change the content of your pages through something like an HTML Proxy, where all the content of your site is filtered through rules and links will magically get changed to include things like /it/ in front of them before they're returned to browsers.
What you should probably do is in your php files, dynamically add a URI base using the contents of the val parameter. So if the request is:
index.php?act=setlang&val=it
index.php will include a
<base href="/it/" />
in the page's headers. That way, your links will appear to be:
http://xxxx.com/it/something.php
I am currently coding a pagination script into many parts of my site, this has been a well needed and requested feature and I have finally been able to come round and start coding it, it is all going well, until I find that my rewritten urls don't like working with the pagination urls.
So, an example page on my site would be news.php. This file structure can be something like news.php?id=5. I have rewritten the url like so:
/news/5/
## Rewrite URL's for News & Dev ##
RewriteRule ^news/([0-9]+)/$ /news.php?id=$1 [L]
RewriteRule ^news/([0-9]+)$ /news.php?id=$1 [L]
RewriteRule ^news$ /news.php [L]
RewriteRule ^news/$ /news.php [L]
The pagination script I am using prepends two new variables in the url, the new url turns out to be this:
news.php?page=1&ipp=55id=5
I would really appreciate it if anyone could assist me in making the urls look better, as it defeats the object of having it in the first place if after they use the pagination, it changes the url back to a clunky and ugly url.
I don't want it to be required to have parts of the url, that is something I really don't want..
e.g I don't want the url to be required to be /news/1/55/5, instead id like it to be optional.
Thank you for your time, it is appreciated!
Additional Information
The links in my news script currently display like so:
news.php?page=1&ipp=55id=5
I don't like to see ugly urls like that, and want to make the url look better using mod_rewrite, It would be better if the urls would display like so:
/news/PAGE/IPP/ID/ -> return news.php?page=1&ipp=55id=5
Also, to make it as user friendly as possible, I don't want any of the fields to be required as such, so for example I would like to have the following link accessible at all times without it requiring the other fields.
/news/ID/
Then, when the user clicks a pagination link, it would use the following link structure:
/news/PAGE/IPP/ID/ -> return news.php?page=1&ipp=55id=5
This is all from user feedback of my site, and is something that people have been asking for. Problem is, I don't understand even simple .htaccess
Thanks
RewriteBase /
# add slash to end of url if not present (and do a redirect)
RewriteCond $0 !/$
RewriteRule ^news([^\.]*)$ $0/ [L,R=302]
# rewrite url with format /news/[<id>/[<page>/[<ipp>/]]]
RewriteRule ^news/(?:([0-9]+)/)?(?:([0-9]+)/)?(?:([0-9]+)/)?$ /news.php?id=$1&page=$2&ipp=$3 [L]
Not sure what ipp is supposed to be, but my guess is it shows the number of item per page. I would personally not like to have that in my url.
You can have :
news/id/page/ipp with
RewriteRule ^news(/?)$ news.php [L]
RewriteRule ^news/([0-9]+)-(.*)_([0-9]+)(/?)$ news.php?page=$1&ipp=$2&id=$3 [L]
news/1222-subjet-for-example_34
return :
news.php?page=1222&ipp=subject-for-example&id=34
use (/?) instead of create many rules ;)
Hope it's works for you.
I have a site written in php which creates 'pretty' urls for each item (on category and search pages) like this,
mysite.com/category/slug-for-item-one/1
mysite.com/category/slug-for-item-two/2
The /category/ and /slug/ is dependent upon the numeric id of the item
I have mod_rewrite serve the actual content from urls like this:
mysite.com/view-item.php?id=1
mysite.com/view-item.php?id=2
The content for each item is retrieved using just the items id.
Here's my htaccess:
RewriteEngine on
RewriteRule ^([^/\.]+)/?$ view-item.php?id=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ view-item.php?pid=$2 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3 [L]
Everythings ok so far but, if someone lands on on a url like,
mysite.com/1
mysite.com/catey/slug-for-item-one/1
or
mysite.com/category/slug-item-one/1
the content is still served, but how can I automatically reset or redirect to the canonical version of the url, to:
mysite.com/category/slug-for-item-one/1
I've searched SO and google extensively for an answer, but no luck. I've only used mod_rewrite for simple redirects such as from without www. to with www. and my understanding is tentative thus I'm struggling to understand how to proceed at the moment.
Can anyone point me in the right direction?
Thank you everyone for your help. Much appreciated. I'm working on an implementation of Jon Lin's answer as I'm more familiar with using php/mysql databases and understand how and why it should work. I aim to be done by Friday and will update this page when finished. Many thanks, Karl.
* UPDATE *
I have implemented Jon Lin's answer and now my 'pretty' urls, when mistyped are now redirected to the correct or 'canonical' url just as on SO. Thank you Jon and everyone who contributed!
I think you are going to need two sets of rewrite rules to accomplish this.
The first set of rules would be used to send 301 redirects to the client to ensure they are referencing the canonical URLs:
RewriteRule ^/1 /category/slug-for-item-one/1 [R=301,L]
Then a second set of rules that use passthroughs [PT] to serve up the content:
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3 [PT,L]
Or something along those lines...
Easy as pie:
RewriteCond %{REQUEST_URI} ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$
RewriteCond %1 !category
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ $1/category/$3/
This means: if the request does look like category/slug-for-item-two/2 and the first match is not the word category (whatever it is) then force the redirect to category/slug-for-item-two/2
Please tell me if it works
Update (after your comment):
Here's what should work:
Create 2 map files (see mod_rewrite.html#rewritemap to learn how to do).
Create a mapfile where you put all the categories you need:
RewriteMap mapcategories \
dbm:/web/htdocs/yoursite/rewriterules/categories.map
In the mapfile create simple entries like:
shirts 1
hats 2
condoms 3
vegetables 4
mother-in-laws 5
...
Now do another file with the opposite:
RewriteMap mapcategoriesreverse \
dbm:/web/htdocs/yoursite/rewriterules/categoriesreverse.map
In the mapfile create simple entries like:
1 shirts
2 hats
3 condoms
4 vegetables
5 mother-in-laws
...
Then here you go for the hard part:
RewriteCond %{REQUEST_URI} ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$
# The following rule will try to search into the categories map file
# and if not found, assign CATEGORY to "notfound"
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ \
- [QSA,E=CATEGORY:${mapcategories:%1|notfound}]
# if the CATEGORY is not empty and is not found:
RewriteCond %{ENV:CATEGORY} notfound
# do a reverse map to get the *real* category:
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ \
- [QSA,E=CATEGORYREVERSE:${mapcategoriesreverse:%1|notfound}]
# if the CATEGORYREVERSE is not empty and is not found:
RewriteCond %{ENV:CATEGORYREVERSE} notfound
# this should never happen => 404:
RewriteRule . - [R=404,L]
# If reach here = if the CATEGORYREVERSE is not empty
# this means it has properly been found:
RewriteCond %{ENV:CATEGORYREVERSE} !^$
# Inject the right category:
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ \
%{ENV:CATEGORYREVERSE}/$2/$3/ [QSA]
This way everything is dynamic but it's (much) longer and (a little bit) more complex.
Olivier
This is probably something you want to implement in your view-item.php instead of trying to use mod_rewrite. When you are generating the links internally within your content, you can use the ID's to lookup categories and slugs. This would be how you normally go about generating one of the pretty SEO friendly links.
You first need pass the category and slug into the request. Something along the lines of:
RewriteEngine on
RewriteRule ^([^/\.]+)/?$ view-item.php?id=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ view-item.php?pid=$2&cat=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3&cat=$1&slug=$2 [L]
At the top of your view-item.php, simply check if the cat and slug parameters exist, compare them to the actual category and slug when you do the lookup for the id. If one of them doesn't match (or is missing, if you want), then redirect the browser to the correct link with the correct category and slug using the header() function:
header('HTTP/1.1 301 Moved Permanently');
header("Location: " . $correct_url);
exit();
After they get redirected, the process repeats itself, but this time, view-item.php sees the correct category and slug so the page gets served like normal.
I have been trying to get my urls to be more user friendly and I have come up with this set up
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/?$ userpage?user=$1 [NC,L]
I added this to my .htaccess but I'm now i'm confused as to how to access these urls.
in my index.php when a user logs in i have tried to redirect the user using
userpage.php?user=s2xi
but the url parses as www.foo.bar/userpage.php?user=s2xi and not www.foo.bar/s2xi
and also tried this as a check to see if user exists (is there a better way?)
if($_GET['user'] != $_SESSION['username']){
header("Location: no_user.php");
}else{
//load page
}
I am using the Smarty template engine on my site and I have my 'themes' in directories that belong to members file
www.foo.bar/users/s2xi/themes
but i want www.foo.bar/s2xi to point to the persons profile page that is viewable by everyone else and not their accounts page.
You're missing the .php in your RewriteRule, if that's verbatim - eg, userpage? => userpage.php?.
However, you're going to run into some problems with this unless you're using a framework to help you distinguish between routes. If you switched to using a separate URI format for user pages (eg /user/foo) you wouldn't have conflicts; but as it stands currently, using .htaccess to rewrite your URLs in that format could potentially cause problems with many other parts of your app.
To do it with a separate URI format, change your last .htaccess line (the RewriteRule) to:
RewriteRule ^user/(.+)/?$ userpage.php?user=$1 [NC,L]
may want to consider adding QSA as well.