I have searched for a solution of this problem all over the Internet, read various threads and articles about it but did come to a full solution for my - i think quite generic problem in mod_rewrite. Here is the issue:
I have developed a small webapp that lets users make calculations (splitting costs after holidays "who pays to whom"). Now I have come to the point where I want to make it (especially the static pages) language dependent. What seemed like no big deal by passing a get parameter ?lang= seems to be a problem for search engines according to my research - so apache mod_rewrite to the rescue to have beautiful URLs like
example.com/en/index => example.com/index.php?lang=en.
example.com/en/about => example.com/about.php?lang=en.
Moreover, users should be able to share their calculations with their friends - for this they are issued an ID after caluclation therefore a rule like
example.com/c/9842398dfalkjdsf98sfdasf => example.com/c.php?id=9842398dfalkjdsf98sfdasf
is used to call their previous calculations (language handling is done here directly in the script automatically, also this links are not needed to be indexed in any search engine).
To achieve this I have come up with these rules:
RewriteEngine on
RewriteRule ^c/([^/]+) c.php?id=$1 [L,QSA]
RewriteRule ^c/([^/]+)/ c.php?id=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/([^/]+)/$ $2.php?lang=$1 [L,QSA]
RewriteRule ^([^/]+)/([^/]+)$ $2.php?lang=$1 [L,QSA]
So far these rules work - however:
Here are my questions:
1) Is this approach a good approach for language dependent sites - meaning will google index the "static" sites like "about" etc. correctly?
2) Come somebody come up with a Rewrite Rule to also have requests like
example.com/about => example.com/about.php?lang=en.
(notice the missing language parameter in the first url)
to send them to the standard language
OR should I then first get their accpted langauge and then redirect them to example.com/LANG/about
3) How should I design the language detection - especially on the homepage? Right now it works according to the rules above - howver I have seen solutions on the Internet passing everything first to a index.php which then call the disred page like
index.php?lang=en&page=about
When google "visits" it will usually not provide an HTTP_ACCEPT_LANGUAGE so will it even ever see the other language versions like example.com/it/about ?
4) Turns out that using RewriteRules kill your relative CSS, JS, picture links in your code (suprise!), however I found a page on the internet saying that this also could be handled with a RewriteRule instead of using absoulute paths in the html? (here). Unfortunately I where not able to implement it.
In a nutshell:
I am a little confused, hope somebody can help how to set up a simple SEO conform, language dependent site and that this will help others to see a best practice solution as a whole.
Thanks in advance!
Try this , thank you
RewriteEngine On
# This is to prevent the rules from looping, they only work as on-shot
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# If the url is blank, go to 'web/en/'
RewriteRule ^/?$ /web/?lang=en [L,QSA]
# If the url starts with en,es,pt-br, remove it and add ?lang=$1 ,has /web
RewriteRule ^/?(en|es|pt-br)/web(/?.*)$ /web$2/?lang=$1 [L,QSA,R]
# If the url starts with en,es,pt-br, remove it and add ?lang=$1 ,has no /web
RewriteRule ^/?(en|es|pt-br)/?$ /web/?lang=$1 [L,QSA,R]
# If the url starts with en,es,pt-br, remove it and add ?lang=$1 ,everything else
RewriteRule ^/?(en|es|pt-br)/(.+?)/?$ /$2/?lang=$1 [L,QSA,R]
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
How can I do to change the name of the url of my site which is: www.example.com/user/panel.php to www.example.com/username/panel.php where the "username" is unique for each user , And for each login would be the name of the user from database, as it is in jsfiddle.net, could they help me?
Personally I would not use .htaccess for this ( specifically )
that said most the time people do it this way
RewriteEngine On
RewriteRule ^users/([-a-zA-Z0-9_]+)/?$ index.php?user=$1 [L]
So if you had a url like
www.yoursite.com/users/someguy
Then it would pass it to apache ( and php ) as
www.yoursite.com/index.php?user=someguy
Then in PHP you could access it just using $_GET[user].
Now ignoring security concerns I may have ( you shouldn't rely on user input to tell who they are, they can lie about it) for this I would use what I call the URI method ( not URL ) a URI is an imaginary path. This is also the method employed by many MVC systems. So for this I will start with the URI
www.yoursite.com/index.php/users/someguy
Notice where the index.php is ( in the middle ). Then you do a .htaccess like this
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f #if not a real file
RewriteCond %{REQUEST_FILENAME} !-d #if not a real folder
RewriteRule ^(.*)$ index.php/$1 [L] #hide the index.php
So what this does is allow you to remove the index.php giving you a url like this
www.yoursite.com/users/someguy
Which is what we want, and looks basically the same as the first case.
Then you cam use the $_SERVER['QUERY_STRING'] supper global which will give you everything past index.php
/users/someguy
And you can split that up, route it somewhere, do whatever you need to with it. Like this
$uri = array_filter( explode('/', $_SERVER['QUERY_STRING'] ) );
//$uri = [ 'users', 'someguy' ];
Now the reason I like this more, is it's more flexible and it lets you use the query string the ?var part of the url for other stuff. ( like bookmarkable search forms ) ie. it feels less hacky because your not breaking the query parameters of a GET Request. Conversely, with the first method, if your .htaccess is sloppy you could make it were the query part of the URL is unusable on your site, and that just feels wrong to me.
It also easier to maintain, because it requires no further setup for additional pretty urls
For example:
Say you want prettyfy your product. Using the first method you would have to go back to the .htaccess add at least 1 more rule in:
RewriteEngine On
RewriteRule ^users/([-a-zA-Z0-9_]+)/?$ index.php?user=$1 [L]
RewriteRule ^products/(0-9_]+)/?$ index.php?product=$1 [L]
Possibly even more complex levels if you have product categories
RewriteRule ^produts/([-a-zA-Z0-9_]+)/(0-9_]+)/?$ index.php?category=$1&product_id=$2 [L]
After a wile you would wind up with dozens of rules in there, some of which may not be immediately clear as to what they do. Then you realize you spelled products as produts and have to start renaming things. It's just a mess later on.
Now using the second method you don't need to do any additional steps, besides routing it in your index page. You just put the url in
www.yoursite.com/products/123
And pull that stuff from the $_SERVER array with no further messing with rewrite rules.
Here is a previous answer I did that outlines how to build a basic router.
Oop php front controller issue
Make sense.
I am trying to rewrite links on my site.
Possible links before rewrite and what I want to see after rewrite:
/index.pl?mode=users&action=add - /users/add/
/index.pl?mode=streets&action=edit&id=7 - /streets/edit/7
/index.pl?mode=users&action=info&id=7 - /users/info/7
etc
.htaccess content:
RewriteEngine On
RewriteCond %{REQUEST_URI} !\.(jpe?g|gif|bmp|png|tiff|css|js)$ [NC]
RewriteRule ^([A-Za-z0-9-]+)/((edit|delete|info))/([0-9]+)$ index.pl?mode=$1&action=$2&id=$3 [L]
RewriteRule ^([A-Za-z0-9-]+)/((add))/?$ index.pl?mode=$1&action=$2 [L]
RewriteRule ^([A-Za-z0-9-]+)/?$ index.pl?mode=$1 [L]
/users/add/ or /street/add/ are working properly, but...
Problems:
/users/edit/xx - I can't accept ID in perl script. Why?
/users/info/xx - I can't even get to info section /?mode=users&action=info&id=7 page (it have to show blank table with wrong ID
Btw... My site has 'switch' structure. I mean if mode is users, then it load "users.pl", if mode=streets loading "streets.pl", etc. About 2nd problem - sure I have info section at users.pl! And link /?mode=users&action=info&id=7 work perfect.
p.s.: added php-tag because it is not 'perl' problem , but php is equal to perl, so php-followers can help me too
p.p.s.: sry for my not very good english.
You have too many parentheses, which makes your backreferences off-by-one:
RewriteRule ^([A-Za-z0-9-]+)/(edit|delete|info)/([0-9]+)$ index.pl?mode=$1&action=$2&id=$3 [L]
RewriteRule ^([A-Za-z0-9-]+)/(add)/?$ index.pl?mode=$1&action=$2 [L]
Those two rules had too many parentheses around the ((edit|delete|info)) and ((add)) (though the "add" isn't affected because $2 correctly backreferences it).
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.