I’m trying to figure out how to combine rewriting and redirecting URLs with .htaccess
. My specific example is: initially I wanted to remove file extensions from page URLs, so I set this up to make /page
display the content of /page.php
:
# URLs without file extension lead to the file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*)$ $1.php [NC,L]
But then I found that if a trailing slash is mistakenly added, it will still cause an error – i.e. /page/
doesn’t work. I’d like someone to be able to navigate to /page/
(because if it gives a 404, that really suggests that /page
doesn’t exist, even though it does) – but I also want to rewrite their URL to remove the slash so the mistake doesn’t happen again.
So, I want two things to happen when someone navigates to /page/
.
- The URL is rewritten to
/page
. - The page loads content from
/page.php
.
While also keeping the rule that navigating to /page
itself will load /page.php
.
Is there a way to combine this behaviour into one rewrite rule? If not, how do I make it happen with two separate rules and prevent feedback loops?
2
Answers
Solved! I added this rule to remove the trailing slash from URLs which aren't directories, before the file extension rule.
The line in Dusan Bajic's comment didn't work on its own - it rewrote the address with the absolute file path instead of the original URL path. But it did find the slash correctly, so I rewrote the substitution to explicitly give the correct address minus the slash.
The “problem” here is this check,
If
page/
was requested, you will have to cut off that trailing slash here, before you append.php
and check if that’s an existing file. But that’s not that easy to do when you only have%{REQUEST_FILENAME}
available, and not much in terms of “string functions” …But, the RewriteRule is evaluated first anyway, so you can achieve this by matching what comes before an (optional) trailing slash inside the rule first, and then use the back reference this creates inside the condition to perform this check:
So far for the theory, not tested in practice 😉 Not too sure whether checking for an existing file works with a relative path only – otherwise, you might have to prefix that with a different variable such as maybe the DOCUMENT_ROOT.
Might be that you need to be a bit more specific in what your rule is allowed to match also, because right now it would allow for something like
folder/page
as well, not sure whether you want to handle that the same way or not.