removed .html extensions with htaccess now index.html give 403 error - .htaccess

After entering the code below, my home page gives a 403 error. The rest of the site works perfectly. All instances of .html were removed.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
Any advice?
Thank you!

example.com leads ti the 403 error. If I write example.com/index it works fine.
Something else must have changed for this to result in a 403 error. The code you posted won't actually do anything when you request example.com/ - the same as if that code didn't exist at all. (UPDATE: However, this assumes your .htaccess file is located in the document - it appears this is not the case - see below.)
However, what will trigger a 403 in such cases is when "formatted directory listings" are disabled and the directory index document cannot be found (or has been disabled).
So, try setting the appropriate directory index at the top of your .htaccess file:
DirectoryIndex index.html
It is the DirectoryIndex that serves the appropriate file when requesting your "home page", not your directives in .htaccess.
UPDATE:
It [.htaccess] is located in my root directory. Would it be better to put it in the public_html folder?
Yes, the code you posted should go in the /public_html directory (ie. your document root). If these directives are in a .htaccess file above the document root then the RewriteRule pattern will match the URL-path public_html/ and rewrite the URL to public_html/.html which is possibly where your 403 error is coming from ("dot" files are usually hidden/protected OS files and you may also have a directive in your server config blocking access. However, this behaviour may also be dependent on other factors in the server config/OS). However, with that code in the document root then a request for example.com/ (your home page) won't be processed by these directives (which is good) - mod_dir should then serve the index.html file in this instance.
However, you don't want to process "directories" anyway (public_html is obviously a "directory", not a file). Which is what's happening above. eg. .html shouldn't be appended to public_html/ to begin with (or example.com/path/to/directory/ or any other directory). This can be avoided by adding an additional condition to your rule block to avoid directories (as well as files). For example:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.]+)$ $1.html [L]
Simply adding that additional RewriteCond directive might be enough and still allow you to keep your .htaccess file above the document root. (However, you may still need to move the .htaccess file as well, as described above.)
Also, the NC flag is not required here and literal dots don't need to be escaped when used inside a character class.
You could also extend this code to first check the existence of the file (with a .html extension) before rewriting, although this may be unnecessary in your case. For example:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^.]+)$ $1.html [L]
This requires an additional "file check" which may be an unnecessary overhead.

Related

Mod_rewrite only working with certain folder names

Context
I'm using mod_rewrite to make my links better for SEO. I made the following rule for my page expanded_debate.php:
Options -MultiViews
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^poll/([0-9a-zA-Z_-]+)/([0-9]+) expanded_debate.php?poll_title=$1&pollid=$2 [NC,QSA,L]
When I input this format in the URL (poll/filename/10, for example) I get a 404 error:
Object not found!
The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again.
If you think this is a server error, please contact the webmaster.
Error 404
localhost
Apache/2.4.46 (Unix) OpenSSL/1.1.1h PHP/7.4.12 mod_perl/2.0.11 Perl/v5.32.0
However, when I change the first folder name to certain words, such as "debate" and "expanded_debate" (but not "expandedebate"), the file loads after page refresh. For example:
RewriteRule ^debate/([0-9a-zA-Z_-]+)/([0-9]+) expanded_debate.php?poll_title=$1&pollid=$2 [NC,QSA,L]
works fine.
I have an older .htaccess file, titled ".htaccess11", with the following info, in case it's of any use:
#forbids users from going to forbidden pages
IndexIgnore *
Options -Indexes
RewriteEngine On
RewriteCond %{SERVER_PORT} !^443$
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/cpanel-dcv/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/(?:\ Ballot169)?
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
#404 error directions
ErrorDocument 404 /404.php
Question
Any idea why only certain terms in the first folder position ("^debate" in example above) work when using mod_rewrite?
There are no "poll" folders in my project, if that's of any interest.
Let me know if there are any questions.
The line
RewriteCond %{REQUEST_FILENAME}\.php -f
Means "Take the requested URL, map it to a full local path in the normal way, append .php to the resulting path, and then process the following rewrite rule only if there is an existing regular file at the modified path".
For example, the URL "poll/filename/10" will be rewritten only if there is a file called "poll/filename/10.php" in the relevant location.
Since the value of the AcceptPathInfo directive is evidently set to On, this condition will also be met if there is an existing file called "poll.php" or "poll/filename.php". That is why the rewrite rule works when you change "poll" to "debate" or "expanded_debate" – there are existing files called "debate.php" and "expanded_debate.php".
In any case, it sounds like this behavior is not what was intended. Removing the -f condition should give the desired result. Or, to prevent the rewrite rule from making existing files inaccessible, you could replace it with:
RewriteCond %{REQUEST_FILENAME} !-f
The exclamation point negates the -f test: "continue only if this file does not exist"
If you are using the %{REQUEST_FILENAME} server variable (anywhere), you should be aware of how the AcceptPathInfo directive will affect this, and consider setting that directive explicitly in the same .htaccess file.
If Options +MultiViews is in effect, then %{REQUEST_FILENAME} will match existing files whether or not the extension is included in the request (GET /foo will match an existing file "foo.php", "foo.html", etc.). And GET /foo.php will match in any case. So, omit the string "\.php" from the original rule.
Other configuration may also have an effect, too. The important point is that, unlike %{REQUEST_URI}, %{REQUEST_FILENAME} invokes all the processing that Apache would otherwise do to translate a URL into a local path.
(source)
NB: although I don't think it was the intention here, you actually might want to test for the existence of a local file as part of this rule. You could use a RewriteCond to check whether the back-end data file for a given poll has been manually created, and return 404 by default if it has not. That would be a simple way to prevent users from making up their own poll URLs at will.

htaccess directory management

im going to ask a really simple question. i dont want my link to show this when i run my page :
http://localhost/example/assets/gallery.php
what i want is :
http://localhost/example/assets/
so how to do it in .htaccess file ?
i would really appreciate it if you can help because im so confused after reading forums .
my htaccess is like this right now but you know it only helps to remove extension :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
How to make assets/gallery.php -> assets/
In the assets folder make a .htaccess file
Paste in this code :
DirectoryIndex gallery.php
This code changes the Directory Index (like a index.php file) to the gallery.php file meaning gallery.php is now like the index.php file.
The DirectoryIndex method that #RyanTheGhost suggests in his answer should have worked for the specific example you posted (where you request a directory and are serving a file from within that directory). However, the mod_rewrite directives you currently have in the document root1 will conflict with any requests for directories2 (although the DirectoryIndex should take priority).
However, the DirectoryIndex method is not very practical if you have many such files. And if you are not requesting a directory then this method naturally won't work anyway.
You could instead rewrite the URL using mod_rewrite in your existing .htaccess file, before your current directives.
1 I'm assuming your .htaccess file is in the document root.
For example:
# Rewrite "/example/assets/" to "/example/assets/gallery.php"
RewriteRule ^example/assets/$ example/assets/gallery.php [L]
Or, to avoid repitition:
# Rewrite "/example/assets/" to "/example/assets/gallery.php"
RewriteRule ^example/assets/$ $0gallery.php [L]
Where the $0 backreference contains the entire URL-path that is matched by the RewriteRule pattern. ie. example/assets/ in this case. NB: There is no slash prefix on the RewriteRule pattern or the substitution string.
Note that since you are requesting a directory (ie. /example/assets/) you need to ensure there is no DirectoryIndex document in that directory (eg. index.html or index.php), otherwise this will be served (by mod_dir) instead, overriding your internal rewrite above.
2 Your current directives that append the .php extension are arguably incorrect:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
This rule appends the .php extension to any request that does not map to a physical file, even if the file with a .php extension does not exist either. This can result in the incorrect URL being reported back to the user in the 404 error document (depending on how this is implemented). For example, the default Apache 404 error document will report that /foo.php does not exist, when the user requested /foo.
This rule will also rewrite directories (since they are "not files") which will result in a 404 (as opposed to a 403 or directory listing, if enabled). Although a DirectoryIndex document will override this.
Additionally, the NC flag is superfluous and there is no need to backslash-escape the literal dot inside a character class.
You could instead check that the corresponding .php file exists before rewriting, instead of checking that the requested URL does not map to a file.
For example:
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.]+)$ $1.php [L]
The request is now only rewritten when the corresponding .php file exists, which naturally avoids any conflicts with directories.

How to rewrite directory for multiple level directories

This is my .htaccess
RewriteEngine On
# browser requests PHP
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.php
RewriteRule ^/?(.*)\.php$ /$1 [L,R=301]
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
RewriteCond %{REQUEST_URI} !^/(views|css|js|media|partials|php)
RewriteRule (.*) /views/$1
This is my project structure:
The idea is that all my HTML files are structured in the folder views, but I don't want my URL to be http://example.com/views/index but just http://example.com/index without the views-part.
This is working fine in the following case:
http://example.com/account/
But fails as soon as I try to access a file in the accounts-folder e.g: http://example.com/account/voeg-kind-toe
That results in a 404. Seems like this .htaccess solution only works for one-level directories.
Edit:
Interesting: If I place the bottom two lines on top (so placing the code to rewrite the view-part before the code to remove the php-extension); the php-extension works but the /views//part don't.
Create .htaccess like this
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteCond %{REQUEST_URI} !^/views/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /views/$1
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteRule ^(/)?$ views/ [L]
As mentioned in my comment above, providing there are no other directives/conflicts then this should still "work" in a roundabout way. However, there is an issue with the following directive in the order you have placed it:
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
You aren't testing for files in the /views subdirectory. But also, REQUEST_FILENAME does not necessarily contain what (I think) you think it does. When you request /account/voeg-kind-toe (an entirely virtual URL path) then REQUEST_FILENAME contains /account (it actually contains an absolute filesystem path, but I've kept it brief). So, the above is testing whether /account.php exists, not /account/voeg-kind-toe.php, or even /views/account/voeg-kind-toe.php - which is presumably the intention.
So, on the first pass, the above condition fails, no rewrite occurs, and processing continues...
The second rule then rewrites the request for /account/voeg-kind-toe to /views/account/voeg-kind-toe. Providing there are no further mod_rewrite directives, the rewrite engine then starts over. This time, /views/account/voeg-kind-toe is the input.
On the second pass, REQUEST_FILENAME is /views/account/voeg-kind-toe (since /views/account is a physical directory) and the request is rewritten to /views/account/voeg-kind-toe.php (since the filesystem check should be successful). Providing you have no other directives then processing should now stop.
This is working fine in the following case: http://example.com/account/
/account/ is simply rewritten to /views/account/ by the last rule.
Edit: Interesting: If I place the bottom two lines on top (so placing the code to rewrite the view-part before the code to remove the php-extension); the php-extension works but the /views//part don't.
The same process as above occurs, EXCEPT this all occurs in a single pass and so is less dependent on other directives that might occur later in the file.
I'm not sure what you mean by "the /views//part don't"?
Assuming you only have .php files within the /views directory and all URLs are intended to target the /views subdirectory and you don't need to reference directories directly then you could do this is a single directive and rewrite everything that does not contain (what looks like) a file extension to /views/<whatever>.php.
For example:
RewriteRule !\.\w{2,4}$ /views%{REQUEST_URI}.php [L]
The L (last) flag is required if you have other mod_rewrite directives that follow - to prevent additional processing.
This does mean you can't rely on the directory index. ie. You need to request /index (as in your example), not simply / to serve /views/index.php.
(You still need your first rule that removes .php from the requested URL - although this is only strictly necessary if you are changing an existing URL structure, where you previously used .php on the URLs.)

.htaccess returns 500 Internal Server Error when slash on filename

My .htaccess file has a rewrite in it so instead of seeing example.com/login.php, one will see example.com/login.
Here is what is written in that file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
It works perfectly except for one thing: I am creating a file called api.php that will serve as the api.
I want clients to go to example.com/api/logout and example.com/api/auth etc. even though api is not a folder but instead a PHP file.
Problem is, whenever I go to a file like example.com/api/logout (or even example.com/login/foo), I always get a 500 Internal Server Error. If I go to example.com/api.php/logout, I get a 404 error.
I checked the error file for the 500 Internal Server Error, and it says Request exceeded the limit of 10 internal redirects due to probable configuration error which makes sense because mod_rewrite runs until all conditions are satisfied.
How should I change my rewrite to fix this?
:
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
When you request a URL of the form /api/logout you will get a rewrite loop (500 error) because the REQUEST_FILENAME maps to <document-root>/api (which if you append .php is indeed a valid file), but you end up appending .php to /api/logout (the URL-path) in the RewriteRule directive (clearly not the same thing). /api/logout.php then gets rewritten to /api/logout.php.php etc. etc. repeatedly appending .php to the URL-path, while all the time checking that /api.php is a valid file.
If all your files are in the document root, then you can change your RewriteRule pattern to only match the first path segment. For example:
RewriteRule ^([^/]+) $1.php [L]
However, if you have files at different directory depths (which you are referencing without the .php extension) then you might need to create some specific rules / exceptions.
If I go to example.com/api.php/logout, I get a 404 error.
This will result in a 404 if additional pathname information (PATH_INFO) has been disabled (although it is usually enabled by default for PHP files). You can explicitly enable this at the top of your .htaccess file:
AcceptPathInfo On

ignore specific directories in htaccess using mod_rewrite

I've got the following code in my .htaccess to strip out index.php from the urls in my CMS-based site.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
This code works great and it routes requests exactly how I want. For example, with URL: http://example.com/contact/ the directory contact doesn't actually exist if you look in the FTP; instead index.php handles the request and shows my contact info. Perfect. Well, almost perfect.
I want to modify this code to specify a couple directories in FTP that should be ignored. For example, if I've got a folder called assets, when I go to http://example.com/assets/ the default DirectoryIndex page is displayed. Instead, I want this directory to be ignored -- I want index.php to handle /assets/.
TL;DR: How can I modify the above code to explicitly ignore certain existing directories (so that index.php handles them instead of the DirectoryIndex)?
Why not adding this below or before your code?
RewriteRule ^(assets/.*)$ /index.php/$1 [L]

Resources