htacess - use optional value in url - .htaccess

I have urls like:
www.example.com => rewritten to www.example.com?type=default
www.example.com/foo => rewritten to www.example.com?page=foo&type=default
www.example.com/bar => rewritten to www.example.com?page=bar&type=default
and I want be able for users to use as well
www.example.com/d/ => rewritten to www.example.com?type=d
www.example.com/e/ => rewritten to www.example.com?type=e
www.example.com/d/foo => rewritten to www.example.com?page=foo&type=d
www.example.com/e/foo => rewritten to www.example.com?page=foo&type=e
www.example.com/d/bar => rewritten to www.example.com?page=bar&type=d
www.example.com/e/bar => rewritten to www.example.com?page=bar&type=e
Currently, for the first I am using simple RewriteRule
RewriteRule ^(foo|bar)$ index.php?page=$1&type=default [QSA,L]
But how can I add the support for specific types?
I can do
RewriteRule ^(d|e)/(foo|bar)$ index.php?page=$2&type=$1[QSA,L]
but is there a way how to write it in a single rule?

You can use regex for it.
"?" Makes the match optional.
RewriteRule ^(d|e)/?(foo|bar)?$ index.php?page=$2&type=$1 [QSA,L]
Reference : https://httpd.apache.org/docs/2.4/rewrite/intro.html#regex

Related

Using htaccess how do I RewriteRule/RewriteCond with no filename?

Hoping this isn't a duplicate, done a lot of looking and I just get more confused as I don't use .htaccess often.
I would like to have some pretty URLs and see lots of help regarding getting information where for example index.php is passed a parameter such as page. So I can currently convert www.example.com/index.php?page=help to www.example.com/help.
Obviously I'm not clued up on this but I would like to parse a URL such as www.example.com/?page=help.
Can't seem to find much info and adapting the original I am obviously going wrong somewhere.
Any help or pointers in the right direction would be greatly appreciated. I'm sure its probably stupidly simple.
My alterations so far which do not seem to work are:
RewriteCond %{THE_REQUEST} ^.*/?page=$1
RewriteRule ^(.*)/+page$ /$1[QSA,L]
Also recently tried QUERY_STRING but just getting server error.
RewriteCond %{QUERY_STRING} ^page=([a-zA-Z]*)
RewriteRule ^(.*) /$1 [QSA,L]
Given up as dead to the world so thought I would ask. Hoping to ensure the request/url etc starts ?page and wanting to make a clean URL from the page parameter.
This is the whole/basic process...
1. HTML Source
Make sure you are linking to the "pretty/canonical" URL in your HTML source. This should be a root-relative URL starting with a slash (or absolute), in case you rewrite from different URL path depths later. For example:
Help Page
2. Rewrite the "pretty" URL
In .htaccess (using mod_rewrite), internally rewrite the "pretty" URL back to the file that actually handles the request, ie. the "front-controller" (eg. index.php, passing the page URL parameter if you wish). For example:
DirectoryIndex index.php
RewriteEngine On
# Rewrite URL of the form "/help" to "index.php?page=help"
RewriteRule ^[^.]+$ index.php?page=$0 [L]
The RewriteRule pattern ^[^.]+$ matches any URL-path that does not include a dot. By excluding a dot we can easily omit any request that would map to a physical file (that includes a file extension delimited by a dot).
The $0 backreference contains the entire URL-path that is matched by the RewriteRule pattern.
The DirectoryIndex is required when the "homepage" (root-directory) is requested, when the URL-path is otherwise empty. In this case the page URL parameter is not passed to our script.
3. Implement the front-controller / router (ie. index.php)
In index.php (your "front-controller" / router) we read the page URL parameter and serve the appropriate content. For example:
<?php
$pages = [
'home' => '/content/homepage.php',
'help' => '/content/help-page.php',
'about' => '/content/about-page.php',
'404' => '/content/404.php',
];
// Default to "home" if "page" URL param is omitted or is empty
$page = empty($_GET['page']) ? 'home' : $_GET['page'];
// Default to 404 "page" if not found in the array/DB of pages
$handler = $pages[$page] ?? $pages['404'];
include($_SERVER['DOCUMENT_ROOT'].$handler);
As seen in the above script, the actual "content" is stored in the /content subdirectory. (This could also be a location outside of the document root.) By storing these files in a separate directory they can be easily protected from direct access.
4. Redirect the "old/ugly" URL to the "new/pretty" URL [OPTIONAL]
This is only strictly necessary (in order to preserve SEO) if you are changing an existing URL structure and the "old/ugly" (original) URLs have been exposed (indexed by search engines, linked to by third parties, etc.), otherwise the "old" URL (ie. /index.php?page=abc) is accessible. This is the same whenever you change an existing URL structure.
If the site is new and you are implementing the "new/pretty" URLs from the start then this is not so important, but it does prevent users from accessing the old URLs if they were ever exposed/guessed.
The following would go before the internal rewrite and after the RewriteEngine directive. For example:
# Redirect "old" URL of the form "/index.php?page=help" to "/help"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/index\.php$ [OR]
RewriteCond %{QUERY_STRING} ^page=([^.&]*)
RewriteRule ^(index\.php)?$ /%1 [R=301,L]
The check against the REDIRECT_STATUS environment variable prevents a redirect-loop by not redirecting requests that have already been rewritten by the later rewrite.
The %1 backreference contains the value of the page URL parameter, as captured from the preceding CondPattern (RewriteCond directive). (Note how this is different to the $n backreference as used in the rewrite above.)
The above redirects all URL variants both with/without index.php and with/without the page URL parameter. For example:
/index.php?page=help -> /help
/?page=help -> /help
/index.php -> / (homepage)
/?page= -> / (homepage)
TIP: Test first with 302 (temporary) redirects to prevent potential caching issues.
Comments / improvements / Exercises for the reader
The above does not handle additional URL parameters. You can use the QSA (Query String Append) flag on the initial rewrite to append additional URL parameters on the initially requested URL. However, implementing the reverse redirect is not so trivial.
You don't need to pass the page URL parameter in the rewrite. The entire (original) URL is available in the PHP superglobal $_SERVER['REQUEST_URI'] (which also includes the query string - if any). You can then parse this variable to extract the required part of the URL instead of relying on the page URL parameter. This generally allows greatest flexibility, without having to modify .htaccess later.
However, being able to pass a page URL parameter can be "useful" if you ever want to manually rewrite (override) a URL route using .htaccess.
Incorporate regex (wildcard pattern matching) in the "router" script so you can generate URLs with "parameters". eg. /<page>/<param1>/<param2> like /photo/cat/large.
Reference:
https://httpd.apache.org/docs/2.4/rewrite/
https://httpd.apache.org/docs/2.4/rewrite/intro.html
https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
RewriteCond %{QUERY_STRING} ^page=([^&]+)
RewriteRule ^$ /%1? [R=302,L]
Can't delete and didn't want to waste anyones time responding.

HTACCESS Rewrite rule merging query parameters into 1st parameter

I am struggling to get the $_GET parameters to sit under their respective key.
I have the following HTACCESS rule:
RewriteRule ^view/new/(.*)/?(.*)/?(.*)/?(.*)$ vehicles/new-details.php?man=$1&model=$2&trim=$3&engine=$4 [QSA]
When accessing this page, I am temporarily printing out the $_GET array:
print_r($_GET);
This returns:
Array ( [man] => BMW/3-Series/SEL/EVO-3.0-TFSI [model] => [trim] =>
[engine] => )
Whereas it should return:
Array ( [man] => BMW [model] => 3-Series [trim] => SEL [engine] => EVO-3.0-TFSI)
The man parameter is required, whereas all others are optional.
Not sure what is happening here. Could it be the fact that I have made parameters optional in the rewrite rule?
RewriteRule ^view/new/(.*)/?(.*)/?(.*)/?(.*)$ vehicles/new-details.php?man=$1&model=$2&trim=$3&engine=$4 [QSA]
(.*) will take in all characters into each capture group, you want to qualify this to take in all characters up to the /.
So replace (.*) with ([^/]*) which is telling the mod_rewrite to "Take any character until you meet a /";
RewriteRule ^view/new/([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)$ vehicles/new-details.php?man=$1&model=$2&trim=$3&engine=$4 [QSA]
This should give you the result you expect.
Edit: Read here for a far fuller answer!

remove directory after main url using htaccess

With the following url http://www.example.com/de/here/ I want to remove the "de" directory (or whatever may be in front of the "here" directory, if anything even is in front of it) so a user is directed to http://www.example.com/here/ instead, which is a directory that does actually exist.
The url could even be something like http://www.example.com/it/here/ or any other combination of 2 letters.
The url could also just be http://www.example.com/here/ in which case I don't want anything removed at all.
I have searched for a solution here but cant seem to make anything work correctly so any help would be much appreciated.
You can use this kind of htaccess :
RewriteEngine On
RewriteBase /
RewriteRule ^[A-Za-z]{2}/(.*)$ $1 [L,R=301]
Example of redirections caused by this code :
http://www.example.com/de/foo/ => http://www.example.com/foo/
http://www.example.com/de/ => http://www.example.com/
http://www.example.com/it/bar/ => http://www.example.com/bar/
http://www.example.com/FR/baz/ => http://www.example.com/baz/
Please note you won't be able to access the language (de, it, fr...) anymore.
Another point, be careful with this kind of url (the redirection will be executed twice) :
http://www.example.com/de/go/ => http://www.example.com/go/
http://www.example.com/go/ => http://www.example.com/
EDIT
Now I've got more details, here is an htaccess you can you to remove the language for specified folders :
RewriteEngine On
RewriteBase /
RewriteRule ^[A-Za-z]{2}/here/(.*)$ here/$1 [L,R=301]
RewriteRule ^[A-Za-z]{2}/anotherfolder/(.*)$ anotherfolder/$1 [L,R=301]

lighttpd htaccess translation !-d !-f and losing get query

I currently try to translate a .htaccess file which is provided by the chyrp installation routine to lighttpd via the mod_rewrite module.
The source .htaccess file is as follows:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase {$index}
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.+$ index.php [L]
</IfModule>
Directory layout (webroot)
/chyrp/
/chyrp/includes/
/chyrp/admin/
/chyrp/feathers/
/chyrp/modules/
/chyrp/themes/
My current attempt
index-file.names = ( "index.php" )
url.access-deny = ( ".twig" )
#taking care of the directory, check if it is one of the known ones, if so go on
#if it is not, redirect to index
url.rewrite-once = (
"^/(|chyrp/)(admin|themes|feathers|modules|includes)(.*)$" => "/chyrp/$2$3",
"^/(|chyrp/).*/$" => "/chyrp/index.php"
)
#check if files exists, if not rewrite to a index.php in the same directory
url.rewrite-if-not-file = (
"^/(|chyrp/)(admin|themes|feathers|modules|includes)(/(index.php)?(.*))?$" => "/chyrp/$2/index.php$5",
"^/(|chyrp/)(.*)" => "/chyrp/index.php"
)
It mostly works, except for the search functionality (testable here: srctwig.com), which does not rewrite correctly (at least that's my guess) or somewhere the query gets lost.
The search routine itself works properly (demonstration search with 0 results)
A working demo of chyrp can be seen at chyrp demo on apache2
What am I doing wrong?
The reason for the issue was, I was not aware of having to dissect the url in order to extract the get query string which I needed to preserve according to a lighttpd mod_rewrite wiki entry
url.rewrite-once = (
"^/(|chyrp/)(admin|themes|feathers|modules|includes)/(.*)$" => "/chyrp/$2/$3",
"^/(|chyrp/).+/(([^\?]+)?(\?.+)?)$" => "/chyrp/index.php$2"
)
url.rewrite-if-not-file = (
"^/(|chyrp/)(admin|themes|feathers|modules|includes)(/([^\?]+)?(\?.+)?)?$" => "/chyrp/$2/index.php$5",
"^/(|chyrp/)(([^\?]+)?(\?.+)?)$" => "/chyrp/index.php$2",
"^/(|chyrp/).*$" => "/chyrp/index.php"
)
Dissect:
url.rewrite-once
"^/(|chyrp/)(admin|themes|feathers|modules|includes)/(.*)$" => "/chyrp/$2/$3",
These directories exist. Do not change anything except for ensuring the proper prefix if not alread there.
"^/(|chyrp/).+/(([^\?]+)?(\?.+)?)$" => "/chyrp/index.php$2"
Check if a non existant subdir was requested and check for ?, treat everything after that as php get query and append it to the rewrite target.
url.rewrite-if-not-file
"^/(|chyrp/)(admin|themes|feathers|modules|includes)(/([^\?]+)?(\?.+)?)?$" => "/chyrp/$2/index.php$5",
if the target does not exist just swap out the filename and keep the query (as above)
"^/(|chyrp/)(([^\?]+)?(\?.+)?)$" => "/chyrp/index.php$2",
go to the basefolder if none of the above matches and try to still pass the query
"^/(|chyrp/).*$" => "/chyrp/index.php"
this very last one is just for testing (put foobarfailure.php there to assert it never reaches that line)

Why does my RewriteRule not work when there is a `?` in the URL

I am learning how to write regular expressions for .htaccess redirects.
So far I've managed to figure out everything I needed, except for a couple of regular expressions which don't behave as I expected. I am testing my regular expressions using a desktop application, and they work fine there, but not in the .htaccess file.
FYI: The RewriteBase is set to /site/
This is the incoming URL:
/site/view-by-tag/politics/?el_mcal_month=3&el_mcal_year=2009
I want to grab "politics" and redirect to /site/tags/politics/
Here is what I used:
RewriteRule ^view-by-tag/([a-zA-Z\-]+)/([a-zA-Z0-9\-\/\.\_\=\?\&]+) /tags/$1/ [R=301,L]
I added the capture of all the characters after politics because I am having the issue that when there is a ? in the URL the redirect does not work, and I can't figure out why. In the URL given above, if I remove the ? it works fine, but if the ? is in there, nothing happens. Is there a reason for this?
The same thing happens when I try to capture 307 from /site/?option=com_content&view=article&id=307&catid=89&Itemid=55
I used this regular expression, article&id=([0-9]+) /?p=$1 [R=301,L] but again, when there is a ? in the URL it stops the redirect for doing anything.
What is the reason for that?
The .htaccess file in question is on a Wordpress blog (3.4.1)
The point that you've missed is that the rewrite engine splits the URI into two parts: the REQUEST_URI and the QUERY_STRING. The query string part isn't used in the rule match string so there is no point in constructing rule regexp patterns to look for it.
You can probe and pick out parameters from the query string by using rewrite conditions and condition regexps to set %N variables.
By default the query string is appended to the output substitution string unless you have a ?someparam in it -- in which case it is ignored unless you used the [QSA] (query string append) parameter.
The way that you'd pick up the id in /site/?option=com_content&view=article&id=307&catid=89&Itemid=55 is to use something like:
RewriteCond %{QUERY_STRING} \bid=(\d+)
Before the rule and this would set %1 to 307. Read the rewrite documentation for more general discussion of how to do this.
The query string is must be processed separately in a RewriteCond if you need to manipulate it, and should not be matched inside the RewriteRule Instead, just match the request not including the query string, and use QSA to append the query string onto the redirect:
RewriteRule ^view-by-tag/([A-Za-z-]+)/?$ /tags/$1/ [R=301,L,QSA]
# OR, if you don't want the rest of the query string appended, put a `?` onto
# the redirect to replace it with nothing
RewriteRule ^view-by-tag/([A-Za-z-]+)/?$ /tags/$1/? [R=301,L]
Actually, the QSA may not be needed in a R redirect - I think that the default behavior is to pass the query string with the redirect.
If you need to capture 307 from the query string, do it in a RewriteCond and capture in %1:
# Capture the id in %1
RewriteCond %{QUERY_STRING} id=([\d]+)
# Redirect everything to /, pass %1 into p
RewriteRule . /?p=%1 [LR=301,L]

Resources