htaccess removing .php extension and pretty urls - regex

Im trying to both remove .php extensions. So for example "http://localhost/timetable/login" instead of "http://localhost/timetable/login.php"
But also have
"http://localhost/timetable/38/" instead of
"http://localhost/timetable/index.php?week=38"
Im able to get one or the other working but not both at the same time. Im assuming its because there is a conflict between them but Im not advanced enough to find it.
Here is my .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*)$ $1.php [NC,L]
RewriteRule ^([0-9]+)$ index.php?week=$2
RewriteRule ^([0-9]+)/$ index.php?week=$2
If in the address bar I type "http://localhost/timetable/38" it brings me to "http://localhost/38/" and an Object not Found error.
Does anyone know what the problem is ?
UPDATE: I can now go to the page but
echo $_GET['week'];
Is returning empty result, so its ignroing the 40 in "http://localhost/timetable/40"

Instead of using separate rewrite rule for each input, you should consider routing all of them as a single string to some php file.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?page=$1 [L,QSA]
In you php file, you can then separate the string as input and use them as required.
<?php
$inputs = explode('/', $_GET['page']);

You only have one capture group when you try to get the week. So it should be $1 instead of $2.
According to this test tool, the following should work:
RewriteRule ([0-9]+)/?$ index.php?week=$1
I would do something like this:
# rewrite if url ends with a number and possibly a slash
RewriteRule ([0-9]+)/?$ index.php?week=$1 [QSA,L]
# do not append .php if it already ends with .php, other add .php
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^(.*)$ $1.php [QSA,L]

Related

htaccess RewriteCond: Why doesn't a server variable match itself?

Perhaps a better question would be, is there any way to use server variables in the matching string?
For example, I can't understand why this fails to match:
RewriteCond %{REQUEST_URI} %{REQUEST_URI}
First, two points.
I know this condition servers no purpose as is.
I know I have poor knowledge of both htaccess and regex.
What I want is to generically turn this URL www.example.com/dir/path/info into www.example.com/dir?foo=/path/info for bootstrapping.
I tried to accomplish this by removing the extra path info from the deepest actual directory in the URL. I was trying this code to test the premise:
RewriteEngine On
Options -Multiviews -Indexes +FollowSymLinks
RewriteBase /
DirectorySlash Off
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} (.+)%{PATH_INFO}
RewriteRule ^(.+?) index.php?dir=%1&path=%2 [L]
No luck. To troubleshoot I reduced it to this:
RewriteCond %{PATH_INFO} (.+)
RewriteRule ^(.+?) index.php?dir=%1 [L]
As expected the query returned foo='/path/info'
So I tried this which I thought would match no matter what:
RewriteCond %{PATH_INFO} %{PATH_INFO}
That failed so as a last attempt, I tried capturing the string:
RewriteCond %{PATH_INFO} (.+)
RewriteCond %{PATH_INFO} %1
That also failed to find a match which has me baffled. %1 should be the complete %{PATH_INFO} string. How could it not match itself???
I don't think it matters but I'm using XAMPP on Windows7 in FastCGI.
Rewrite pattern params only allow regex (tho Condpattern also has special flags for tests and comparisons):
RewriteCond TestString CondPattern
RewriteRule Pattern Substitution
Server variables like %{REQUEST_URI} can only be used in Teststring and Substitution. The following docs outline this usage:
http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html#rewritecond
http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html#rewriterule
If this will go in your main .htaccess, perhaps try:
RewriteCond %{REQUEST_URI} !index\.php$
RewriteRule ^([^/]+)/(.+)$ index.php?dir=/$1&path=/$2 [L]
Two more Example:
Sample1
RewriteBase /
RewriteRule ^(.+/)?index.php(/.+) index.php?dir=/$1&path=$2 [R,L]
Sample2
RewriteBase /
RewriteRule ^((.+/)?index.php)(/.+) $1?path=$3 [R,L]
Sample3
RewriteBase /
RewriteRule ^(.+/)?(.+\.php)(/.+) $1$2?foo=$3 [R,L]
these all do external rewrite so you can see result in the browser address. To revert to internal rewrite, just remove [R] flag
Ok, I found a way to accomplish this goal.
Basically I was trying to compare two server variables. htaccess won't do that. I wanted to extract part of a "pretty" url which points to an actual file or folder. The variable ${SCRIPT_URL} should do that, but it is either depreciated or not reliable. The work around is to put both variables in the test string and use a regex back reference to find the point of duplication.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_URI}%{PATH_INFO} (.*?)(/.+)\2$
RewriteRule ^(.*)$ %1.php?strappath=%2 [QSA,END]
In the above example %1 will be the uri of the file and %2 will be the remaining path after the URI, duplicating %{PATH_INFO}.
Follow with this rule for when there is no extra path info
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)$ $1.php [QSA,END]
If no .php file is found, I want the index of that directory and add the un-found file name to the pathinfo. This is a bit trickier.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php !-f
RewriteCond %{REQUEST_FILENAME} ^(.*)(/.+)$
RewriteCond %1 -d
RewriteCond %1/index.php -f
RewriteCond %{REQUEST_URI}%{PATH_INFO} ^(.*?)(/.+)\2$ [OR]
RewriteCond %{REQUEST_URI} ^(/.+)(/.+)?$
RewriteCond %1 ^(.*)(/.+)$
RewriteRule ^(.*)$ %1/index.php?strappath=%2%{PATH_INFO} [QSA,END]
The above section fails to catch urls that point directly to an existing folder with an index.php, so to catch those:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME}/index.php -f
RewriteCond ^(.+)$ $1/index.php [QSA,END]
I doubt anyone ever finds this useful but I've seen variations of this question asked over and over with no working solution given.

.htaccess: Redirect removing file extensions unless part of a query string

Many answers here come close but none are complete.
I would like .htaccess to redirect and remove php/html extensions and trailing slashes so all files, directories, and bootstraps look the same. The problem is that a URL might, or might not, include a query string with an external URL link which therefore should not be altered.
Scenario 1: Remove ".php" from simple URLs.
This: examle.com/file.PHP
Becomes: examle.com/file
Scenario 2: Remove ".php" from URLs but not the query.
This: examle.com/file.PHP?link=anothersite.com/somefile.PHP
Becomes: examle.com/file?link=anothersite.com/somefile.PHP
I have rewrite conditions which work for one or the other scenario but can find no single solution for both.
This condition works for scenario 1 but fails in scenario 2 in that it also strips the extension from the link in the query string.
RewriteCond %{THE_REQUEST} \/(.+)\.php
This (with a \?) works in scenario 2 but ONLY works if there is a query (a question mark) so it fails in scenario 1.
RewriteCond %{THE_REQUEST} \/(.+)\.php\?
The solution would be a regular expression which looks at the whole string unless there is a question mark. Or possibly somehow parses the string at any question mark. I've tried putting a "zero or one" qualifier at the question mark but it caused a redirect loop.
The completed code looks something close to this.
# Remove extensions shown in address bar
RewriteCond %{THE_REQUEST} "\/(.+)\.(php|jar|html|shtml|htm) [NC]
RewriteRule ^ /%1 [L,R=301]
# Remove trailing slashes
RewriteRule ^(.*)/$ /$1 [R=301,L]
# Display php file if it exist
rewriteCond %{REQUEST_FILENAME}.php -f
rewriteRule ^(.+)$ $1.php [L,QSA]
You can use this redirect rule to make it work for both URLs:
RewriteCond %{THE_REQUEST} \s/+([^?]+?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=302,NE,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [NE,R=302,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
I've included this final code for any who come here with similar issues related to clean & pretty URLs.
The code below should:
Remove trailing garbage (slashes, question marks, dots etc...)
Hide unwanted extensions and indexes
Display the related index.php / filename.php
Thanks to anubhava for the needed solution.
RewriteEngine On
Options -Multiviews -Indexes +FollowSymLinks
RewriteBase /
DirectorySlash Off
# Remove trailing question marks.
RewriteCond %{THE_REQUEST} \?[\s?] [NC]
RewriteRule ^(.*)$ /$1? [R,L]
# Remove unwanted extensions, dots, "index", punctuation, trailing slash
# First condition group removed from anywhere in the path.
# Second condition group only removed from the end of the path.
# The rule will not alter text in the query.
RewriteCond %{THE_REQUEST} \s/+([^?]+?)\.(php|jar|html|shtml|htm)([^\s]*?)[\s?] [NC,OR]
RewriteCond %{THE_REQUEST} \s/+([^?]+?)(\/index|\/|\.|,|\!])[\s?] [NC]
RewriteRule ^ /%1%3 [R,NE,L]
# If path points to a directory containing an index.php, use it
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME}/index.php -f
RewriteRule ^(.+)$ $1/index.php [L]
# ElseIf path/filename points to a php file, use it
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
################################################################
# Below here is only needed if you want a bootstrap
# Without this, files will display or 404 as normal.
################################################################
# If none of the above conditions are met,
# and the request is not an actual file (.jpg, .svg, etc.),
# display the bootstrap file making the path
# a query string for the bootstrap to parse.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?strappath=$1 [QSA,L]
Thanks and please let me know if there are errors or loopholes.

What is the error in this .htaccess?

Using mod_rewrite, I want to look for all requested files in an assets directory, and otherwise fallback to index.php for API calls. This is my .htaccess.
RewriteEngine on
RewriteRule ^.+\.[^/]+$ assets/$1 [L]
RewriteRule .* index.php
Asset files are determined by whether the last last url segment has a file extension, which means a dot without following slashes. I already tested the regex at regexpal and it seems to work fine.
Unfortunately, all request result in a 500 Internal Server Error using these rewrite rules. I know that mod_rewrite is set up correctly, since omitting the second line works as expected.
Try these rules:
RewriteEngine on
# rewrite to assets/file if file exists in assets dir
RewriteCond %{DOCUMENT_ROOT}/assets/$1 -f
RewriteRule ^(.+?\.[^/]+)/?$ assets/$1 [L]
# otherwise rewrite to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^assets/ index.php [NC,L]
You need to add a condition to prevent the rewrite engine from looping:
RewriteEngine on
RewriteCond %{REQUEST_URI} !index\.php
RewriteRule ^.+\.[^/]+$ assets/$1 [L]
RewriteCond %{REQUEST_URI} !index\.php
RewriteRule .* index.php
The rewrite engine loops until the URI stops changing, and since .* matches index.php as well, it'll continue to loop through that rule until the internal recursion limit is reached you get a 500 error.
Additionally, you can add conditions that won't reroute existing files or directories:
RewriteEngine on
RewriteCond %{REQUEST_URI} !index\.php
RewriteRule ^.+\.[^/]+$ assets/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php

regex mod_rewrite ?-mark sign causing bug

I'm trying to redirect old-style links from an old website to new style links in php.
I'm using:
RewriteEngine on
RewriteBase /
RewriteRule s\.cfm?id=5$ http://mysite.com/article5.php [B,L,R=301]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
However, if I use just s.cfm? everything works fine and redirects to article5.php. But if I try to redirect the id=5 part, I get page not found.
I tried just s.cfm?id and it causes the htaccess bug. So anything you put after question mark ?... causes a problem, I don't know why.
You can't match against the query string inside a RewriteRule, only the URI path is sent through the rule itself. If you need to match against the query string, then use the %{QUERY_STRING} var inside a RewriteCond:
RewriteCond %{QUERY_STRING} ^id=5$
RewriteRule ^/?s\.cfm$ http://mysite.com/article5.php [L,R=301]
Everything else is fine.

Removing index.php from url using .htaccess even if the user requests it

I never want index.php to show up in my URL, even if the user inputs it. Is this possible?
This is variation whatever after several tries. I've come close a few times but this is where it's at for now.
RewriteEngine On
RewriteBase /sub-dir/
RewriteCond %{REQUEST_URI} index\.php$ //If URL ends in index.php
RewriteRule (.*)index\.php $1 //Somehow remove index.php from the url
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
Currently I have permalink set up where if the user enters domain.com/sub-dir/my-perma-lin/ it generates a string on the page based on my-perma-link to look like My Perma Link. What I'd like is if the user submits any URL ending in index.php it just removes that from the URL but leaves everything else as is.
domain.com/sub-dir/index.php --> domain.com/sub-dir/
domain.com/sub-dir/my-perma-link/index.php --> domain.com/sub-dir/my-perma-link
I've written quite a few rules in http://htaccess.madewithlove.be/ that work perfectly but when I upload it (to Dreamhost) nothing works.
This for example should work according to the the tester
RewriteEngine On
RewriteBase /sub-dir/
RewriteCond %{REQUEST_URI} \.php //Not needed but thought it would/should help
RewriteRule (.*)(index\.php)+ $1 [L,R=301,NC]
But it just removes everything after /sub-dir/
I'm either missing something super obvious or it's not possible ...
You need to add some flags to your rule:
RewriteEngine On
RewriteBase /sub-dir/
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(.*)index\.php(\?|\ )
RewriteRule ^ /%1 [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
You can ditch the RewriteCond %{REQUEST_URI} index\.php$ condition, as that's being checked by the regex in the RewriteRule. You need to include a $ at the end of the regex, and include the flags L to stop rewriting and R=301 to redirect.