mod_rewrite for friendly URL's - apache

I'm trying to implement Permalinks into my content management system and I seem to be stuck at a problem with mod_rewrite. I have a PHP file (single.php) which will display a single post based on the Permalink name that gets passed through to it. i.e. post.php?permalink=name-of-post-here.
Here are the rules I've set up:
RewriteRule ^([0-9]{4})/([a-z]+)?$ $1/ [R]
RewriteRule ^([0-9]{4})/([a-z]+)?$ post.php?permalink=$1
Also, how do I get the date/year (i.e. 2012), and assign that as a parameter for the PHP file. (i.e. post.php?year=2012?permalink=name-of-post-here) & can all of this be done using mod_rewrite?
Many Thanks.
Want to change
http://www.website.com/post.php?year=2012&permalink=post-name-here
to
http://www.website.com/2012/post-name-here

You should use this code:
Options +FollowSymLinks -MultiViews
RewriteEngine on
RewriteRule ^([0-9]{4})/(.*)$ post.php?year=$1&permalink=$2 [L,R,NC,QSA]
If you don't want external redirect (change URL in browser) then remove R flag.

Related

Why is this RewriteRule altering QUERY_STRING, but leaving REQUEST_URI untouched?

I have a copy of Concrete5, a PHP-based CMS, running on example.com.
Concrete5 comes with the following basic instructions for pretty URLs (redirecting all URLs to a central index.php)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L] # Concrete5 is running in the c5.7/ subdirectory
</IfModule>
Pretty straightforward.
Now I have a certain set of URLs that take the form
/product/{productname}
that I need to forward to the Concrete5 (virtual) URL
/products/details?name={productname}
That URL is set up and works as expected when I enter it manually in the browser.
So I added a line to the htaccess file and it now looks like this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ /products/details?name=$1 [QSA]
RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L]
</IfModule>
I can confirm the RewriteRule gets triggered when I choose a random, external URL as the redirection target.
But whenever it is an internal redirect like above, what happens is, I get a 404 inside Concrete5. When I inspect what was passed to it, I see:
REQUEST_URI: /product/my-random-product
QUERY_STRING: name=my-random-product
So it appears that the rule is triggered and does some rewriting, but REQUEST_URI remains unchanged!
Why?
Is it because PHP 7.1 is running via CGI?
I have tried a zillion variations and all the flags in the book, with little success.
The REQUEST_URI in PHP is not the same as the REQUEST_URI within mod_rewrite, so you can't do it like this. In PHP it always contains the original URL. So you can't change it like this if your CMS is working off that.
You should set up your CMS to use the URLs you want, rather than trying to augment your CMS's URL rewriting like this.
If you inspect REDIRECT_URL in PHP you will see the last rewritten URI.
REQUEST_URI in PHP will always be the original request URI.
Because this is already explained by LSerni and SuperDuperApps, I won't elaborate.
Instead, I'm offering a quick solution: modify the REQUEST_URI and add a name parameter in PHP instead of in .htaccess.
Add the following code to the start of your Concrete5 index.php to make sure that REQUEST_URI is modified
before any Concrete5 code runs:
if(preg_match('-^/product/([^?]*)-',$_SERVER['REQUEST_URI'],$matches)){
$_SERVER['REQUEST_URI'] = '/products/details';
$_GET['name'] = $matches[1];
}
Your setup works on a PHP 7.1 machine (without Concrete5). It does call a script I just put in, which is in /c5.7/products/details. So the Apache part is working.
Inside the script, I see that REQUEST_URI is the old value prior to the rewrite.
So its value is normal and it not being rewritten is a red herring - it isn't supposed to be rewritten. The 404 error must be due to something else.
Your Concrete5 routing should support the real URL, not just the virtual one, because C5's routing relies itself on REQUEST_URI. If this is so, you need to create a route for your short URLs
Route::register('/product/{productname}' ...)
and an appropriate controller to get the parameters and invoke the "old" controller.
One possibility using .htaccess could be this, but I'm not too sure it will work since REQUEST_URI is still left unchanged:
# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ c5.7/products/details?name=$1 [L,QSA]
Otherwise you need to do an external redirect, which will disclose the URL in the browser:
RewriteRule product/(.*)$ http://.../products/details?name=$1 [QSA]
See also this other question.

mod_rewrite in .htaccess file for unknown first part of path

I am trying to write a rewrite rule to change path slugs to query parameters. It is for a web service, and should only rewrite this rule if the host starts with api. There are two slugs that I am trying to capture and rewrite. The first is optional and is a version (i.e. v1.2) and the second is the service domain (i.e. customers, transactions, etc.).
http://api.domain.com/v2.5/customers should rewrite to ?version=2.5&domain=customers
I also want to support a default version so that
http://api.domain.com/customers should rewrite to ?version=&domain=customers
Here is what my .htaccess file looks like:
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^api\..*
RewriteRule ^v([\d\.]*)?\/?([^\/]*)$ ?version=$1&domain=$2
The first example above works fine, but I can't get the default version path to work. I have tried a ton of different things. I thought starting with ^.*v would help, but it didn't. Anybody know how to make it match when you don't know the starting characters?
Try:
RewriteCond %{HTTP_HOST} ^api\..*
RewriteCond %{QUERY_STRING} !version=
RewriteRule ^(v([\d\.]*))?\/?([^\/]*)$ /?version=$2&domain=$3 [L]
This makes the /v part optional:
/v2.5/foo -> /?version=2.5&domain=foo
/foo -> /?version=&domain=foo
/v/foo -> /?version=&domain=foo

How can I add an additional word to a URL using mod_rewrite

I have an application with URLs such as these:
https://www.domain.com/example/public/subscription
https://www.domain.com/example/public/subscription/source
https://www.domain.com/example/public/test/subscription
I'd like to use mod_rewrite (or some other method) to create shorter "aliases" of the above URLs by removing the /public/ part so that I can provide my client with these shorter versions of the URLs:
https://www.domain.com/example/subscription
https://www.domain.com/example/subscription/source
https://www.domain.com/example/test/subscription
In other words, when browsing to https://www.domain.com/example/subscription, for example:
The server must send back the same response that one would get when opening https://www.domain.com/example/public/subscription directly
The browser must still display the shorter version of the URL (without the /public/) in the address bar
Is this even possible, and how would such a RewriteRule look like?
Thank you in advance.
put this is .htaccess file in your DocumentRoot.
Options +FollowSymLinks -Indexes -MultiViews
RewriteEngine on
RewriteBase /
RewriteRule ^(example)/(.*)$ $1/public/$2 [NC,L]
You just want to "remove" the public keyword?
Try this rewrite rule:
RewriteRule ^/example/public/(.*) /example/$1 [L]

How to catch all characters with slashes after domain address with mod_rewrite as one parameter?

How to catch all characters after domain name as one parameter with mod_rewrite?
I have script which load other page content and displays that like standard page.
example script address look like that:
example.com/script.php/?www='/some/paths/etc'
I want to mask that URL to look like this:
example.com/some/paths/etc
If no paths exist it should look like
example.com/
There can be different amount of URL parts (a/b/c/.../X/)
I red about mod_proxy but I can do it with only .htaccess so it doesn't solve my problem.
I can't redirect one address to other, they have to be separated.
You can proxy via mod_rewrite (as long as mod_proxy has been compiled into the server), and this can be done via .htaccess files. Note that RewriteEngine On requires Options FollowSymLinks to be overridable in your .htaccess.
The following in your .htaccess file:
Options FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/script.php/?www=
RewriteRule ^/script.php/?www='([^']+)' http://example.com/$1
RewriteRule ^/(.*) /script.php/?www='$1' [P]
The RewriteCond matches your script path with a ?www= argument (if it fails to match no rewrite will take place). The first RewriteRule will match the www= argument and rewrite the visible path of the request. The second RewriteRule will then proxy the request (due to the [P] flag) to the script and will get the "content".
Note: this has not been tested, but hopefully will get you heading in the right direction to solve the problem. Also if script.php expects optional args &var2=whatever you will probably need to tweak the RewriteCond to match that and store it using parentheses. You can then recall those args using %1. More details can be found in the documentation.
Also to help you debug your rewriting you can use:
RewriteLog /full/path/to/your/log/file
RewriteLogLevel 3
EDIT: Added the RewriteLog stuff an clarified that the snippet at the top is to be put in the .htaccess file

Rewriting a redirected URL with mod_rewrite

Here is my setup :
I have a website located at www.cabsh.org/drupal
I want to use mod_rewrite to do 2 things :
Redirect www.cabsh.org to http://www.cabsh.org/drupal/index.php (I got this one)
Rewrite /www.cabsh.org/drupal/index.php to www.cabsh.org/site/index.php
I cannot figure how to achieve the 2nd point. I'm using .htaccess files since I cannot use the main server configuration. Can anyone help me getting this to work?
Thanks!
From what I get from your comment, you just want something like this:
RewriteEngine on
# Prevent a request directly to the /drupal folder
RewriteCond %{THE_REQUEST} ^[A-Z]+\s/drupal/
RewriteRule ^drupal/(.*)$ /site/$1 [R=301,L]
# Change a request for /site/(anything) to /drupal/(anything)
RewriteRule ^site/(.*)$ /drupal/$1
Be careful though, since Drupal (being in the Drupal folder) might generate links that point to /drupal instead of /site, which is seemingly not what you want.