I have clean path with the same name as existing directory.
I use these .htaccess rules to support clean path for the path:
RewriteCond ${REQUEST_URI} ^/mydir
RewriteCond ${REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
everything works correctly (I have a "mydir" clean path working and I can access existing files in the /mydir directory directly), but apache appends the trailing slash all the time to requests.
I request http://domain.com/mydir, and it redirects me 301 to http://domain.com/mydir/.
What is the reason?
Trailing slash after /mydir/ is added by an Apache module called mod_dir that adds a trailing slash after all the directories. This is due to this setting turned on by default:
DirectorySlash On
You can turn it off using:
DirectorySlash Off
However it might expose some directories by showing their listings.
Security Warning
Turning off the trailing slash redirect may result in an information
disclosure. Consider a situation where mod_autoindex is active
(Options +Indexes) and DirectoryIndex is set to a valid resource (say,
index.html) and there's no other special handler defined for that URL.
In this case a request with a trailing slash would show the index.html
file. But a request without trailing slash would list the directory
contents.
Apache's proper URL always ends in a slash /. Because it treats URL's as if they were a disk file path (which always ends in a slash). If it's not there, the server needs to take one additional step to internally add it. I say let it be.
Plus Google (supposedly) likes the trailing slashes.
I say keep it as is.
Please read more: http://cdivilly.wordpress.com/2014/03/11/why-trailing-slashes-on-uris-are-important/
and here: http://bit.ly/1uSvbfy :)
Related
I'm building a simple PHP router and I understand the most things except for the things in the ".htaccess" file. If I comment out the things in the ".htaccess" file, nothing changes, or I don't notice anything changing(the contents of the ".htaccess" file is from a tutorial). The ".htaccess" file contains the following things
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php [QSA,L]
and this is the content from the index.php
$request = $_SERVER['REQUEST_URI'];
switch ($request) {
case '/ROUTER/index.php':
case '/ROUTER/index.php/home':
require __DIR__ . '/controller/homeController.php';
break;
case '/ROUTER/index.php/about' :
require __DIR__ . '/controller/shopController.php';
break;
default:
http_response_code(404);
require __DIR__ . '/controller/404Controller.php';
break;
}
the contents in the homeController.php etc files are just a simple echo with a text. Maybe someone can explain to me what happens in the ".htaccess" file.
#StephenOstermiller's answer has done a great job of describing the .htaccess file / front-controller process, but I thought I'd address the other issue you raised...
If I comment out the things in the ".htaccess" file, nothing changes
That's because the format of the URLs you are using (eg. /ROUTER/index.php/home) completely negates the need for the .htaccess file to begin with. You are calling index.php (the "front-controller") directly in the URL itself and passing /home as additional pathname information (aka. path-info).
The .htaccess file is still processed, but the 2nd condition (RewriteCond directive), which checks that the request does not map to a physical file, fails (index.php is a physical file) so the rule is not triggered (it does nothing).
The additional path-info on the URL is available to PHP in the $_SERVER['PATH_INFO'] superglobal. So, if you are using URLs of the form /ROUTER/index.php/home then you could write your front-controller (index.php) like this instead (simplified):
// URLs of the form "/ROUTER/index.php/home"
$request = $_SERVER['PATH_INFO'];
switch ($request) {
case '':
case '/home':
:
case '/about' :
:
}
(As noted above, this is not making use of .htaccess)
On the other hand, your .htaccess file allows you to have URLs of the form /ROUTER/home (or simply /home), avoiding you having to include index.php in the URL, which is then internally rewritten to index.php (by the rule in .htaccess). You then use $_SERVER['REQUEST_URI'] (as in your original script) to access the requested URL in PHP.
For example (simplified):
// URLs of the form "/ROUTER/home"
$request = $_SERVER['REQUEST_URI'];
switch ($request) {
case '/ROUTER/':
case '/ROUTER/home':
:
case '/ROUTER/about' :
:
}
However, your existing .htaccess file is not configured correctly for this. The .htaccess file is assuming index.php is located in the document root, but your URLs suggest you have a /ROUTER subdirectory, in which index.php (the "front-controller") is located.
If your .htaccess file is in the /ROUTER subdirectory at /ROUTER/.htaccess then remove the RewriteBase directive entirely.
If, however, your .htaccess file is located in the document root then you will need to change your RewriteBase directive to read:
RewriteBase /ROUTER
(Setting RewriteBase /ROUTER in both cases will also work.)
Most web servers have logic like this for determining which file to serve:
If a file exists with a file name that matches the URL path, use that file
If a directory exists with a name that matches the URL path:
If a default file (usually index.html or index.php) exists in that directory, use that file
If configured to show a file listing, do so
Otherwise the web server returns an error status.
Many web applications want to implement their own logic for what to show for each URL and implement their own routing rules. They typically use .htaccess to create a Front controller that passes nearly every request into a single central file that handles its own routing for the web application. Your .htaccess has fairly standard front controller rules that pass most URLs into index.php
Breaking it down line by line:
RewriteEngine On -- Enables mod rewrite which is an Apache module that uses rules to change which file handles which URLs
RewriteBase / -- Specifies which directory the rules are relative to. This can usually be omitted because / is the default value in .htaccess in the document root. See What does RewriteBase do and how to use it?
RewriteCond %{REQUEST_FILENAME} !-d -- If the requested URL is not an existing directory
RewriteCond %{REQUEST_FILENAME} !-f -- If the requested URL is not an existing file
RewriteRule ^(.+)$ index.php [QSA,L] -- Handle all URLs with index.php. A rewrite rule has three sections:
The pattern to match against the URL path (^(.+)$)
^ is starts with
.+ one or more of any character
$ is ends with
() parenthesis capture the matched characters as a group that is available in a variable ($1). The parenthesis in this rule could be removed because the variable is never used.
The target file that handles the URLs (index.php)
Flags that modify how the rule works ([QSA,L])
QSA is query string append to make sure index.php has access to anything after the ? in the URL
L is last so that any later rewrite rules wouldn't also get executed.
In effect, it uses the default web server logic for files or directories that exist, but pass all other requests to index.php.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
i am newbie of php
How to create simple remove .php extension with .htaccess page?
My aims is type this url http://site/Test
it can visit hello.php content, anyone know how to do that?? thx
I have one folder that call "Test", there are two file on "Test" Folder
Test/
-hello.php
-.htaccess
hello.php
<?php echo "Hello World"; ?>
.htaccess
RewriteEngine on
RewriteRule ^(.*)$ $1.php
finally, it is not successful
it display the following error message
**Internal Server Error**
The server encountered an internal error or misconfiguration and was unable to complete your request.
anyidea to solve this??? Thank you very much
My aims is type this url http://site/Test it can visit hello.php content, anyone know how to do that?
This isn't simply "file extension removal". If it was then /Test would serve the content from /Test.php. In your example, you are wanting to serve the contents of an entirely different file.
Rewriting the request with mod_rewrite
Since you are requesting a directory (/Test/) and wanting to serve a file from within that directory and the .htaccess file itself is actually located in that directory, you would need to write your RewriteRule like this:
RewriteRule ^$ hello.php [L]
In .htaccess (directory context), the RewriteRule pattern matches the URL-path relative to the location of the .htaccess file. So, in this case the URL-path we are matching against is simply an empty string, ie. ^$, since we are wanting to match /Test/<nothing>.
Likewise, when the susbtitution string (ie. hello.php) is relative (as it is here), it is relative to the location of the .htaccess file. So, in this case it effectively rewrites the request to /Test/hello.php (it's actually the absolute filesystem path, eg. /var/www/user/public_html/Test/hello.php - after the directory-prefix is added back).
Additional issues with this example
Since you are rewriting a request that would otherwise map to a physical directory there are a couple of potential issues you need to be aware of...
You should request the directory with a trailing slash, ie. /Test/, not /Test (as you stated in your initial example), otherwise mod_dir will issue a 301 redirect to append the slash before your rewrite is successful.
(There are ways to avoid the trailing slash, but this does increase the complexity and requires further manual rewrites.)
An additional complication occurs if there is a DirectoryIndex document in this subdirectory. eg. /Test/index.php. In this case mod_dir issues an internal subrequest to the DirectoryIndex document (eg. index.php) and this takes priority over your internal rewrite. (Your rewrite does still occur, but mod_dir "wins".) If this is the case then you can rewrite the DirectoryIndex document instead of an empty URL-path. For example:
RewriteRule ^index\.php$ hello.php [L]
This is perhaps counter-intuitive, as we are now rewriting the internal subrequest that mod_dir has issued in a later pass through the file.
You could handle both scenarios and make index.php optional. For example:
RewriteRule ^(index\.php)?$ hello.php [L]
RewriteRule ^(.*)$ $1.php
Your example would result in an internal rewrite loop (500 Internal Server Error response) since the pattern ^(.*)$ also matches the rewritten URL and it gets stuck in an endless loop. (The rewriting process doesn't just consist of a single pass through the file. The process repeats until the URL passes through unchanged.)
(Incidentally, this IS an extensionless URL type of rewrite, but it doesn't help you achieve what you stated in your example.)
There are various ways to prevent this "endless loop":
Use a more specific regex, that won't also match the rewritten URL. eg. a regex that excludes a dot such as ^([^.]+)$.
Use a RewriteCond (condition) directive that prevents the rule being triggered on the rewritten URL. eg. Exclude .php files or check that the request does not map to a file, etc.
Use the END flag on the RewriteRule to stop all further processing by the rewrite engine.
Alternative - change the DirectoryIndex
Instead of using mod_rewrite, as explained above, to internally rewrite the request, we could instead change the DirectoryIndex document.
This only works in this particular case where you are requesting a directory and wanting to serve a file from that directory (although strictly speaking the file could be anywhere).
The DirectoryIndex is the document that mod_dir will look for when requesting a directory (eg. /test/). By default, it looks for index.html (and often index.php) and possibly others. If a DirectoryIndex document is not found, you get a 403 Forbidden when requesting that directory (assuming auto-directory indexes are disabled).
For example, you could set the following:
DocumentIndex hello.php
And now when you request /Test/, mod_dir will serve hello.php in that directory.
However, this method (by itself) is limiting and potentially confusing for readers of your code (if changing the DirectoryIndex on a directory by directory basis). It is generally expected that the DirectoryIndex document(s) is consistent throughout your site.
The weirdest thing. Both rewrite rules are working on our Test server but the second one locations is not working on Production and adds a / at the end of locations/ for some reason. The configuration is the same on both servers.
I am trying to simplify the URL without renaming the file. So that if the user enters site.php/locations it will load the content of the redirect page but retain the simplified URL
Any ideas as to why the locations redirect is not working?
RewriteEngine on
RewriteBase /
// The next two are statements are just for context. They preceded the faq and locations forward
RewriteCond %{REQUEST_URI} ^/index.php$
RewriteRule (.*) / [R=301,L]
// The two rewrites that I want
RewriteRule ^faq$ /faq-mailbox-account.php [NC,L]
RewriteRule ^locations$ /choose-digital-mailbox-location.php [NC,L]
... on Production and adds a / at the end of locations/ for some reason
This will happen if locations exists as a physical directory on the filesystem. (Or existed and the response was cached.)
If you request /locations and /locations exists as a physical directory then mod_dir will issue a 301 external redirect to append the trailing slash - ordinarily, this is necessary in order to "fix" the URL.
This external "redirect" by mod_dir will happen regardless of whether your rule matches or not*1, since it is "only" an internal rewrite and not another redirect. (*1 In fact, it does match and the rewrite does occur, but it is effectively overridden by the mod_dir redirect.)
You could append an optional slash to your RewriteRule pattern, eg. ^locations/?$ - but the redirect will still occur first if you are requesting /locations without the trailing slash. You would need to request a URL that already contains the trailing slash.
You could prevent mod_dir from appending the trailing slash using DirectorySlash Off, however, this potentially has other implications for your system.
Note also, that since this is a 301 (permanent) redirect, it will likely be cached persistently by your browser. So, if the /locations directory has since been deleted on the server, the browser will still redirect the user until all client-side caches have been cleared also.
The configuration is the same on both servers.
So, there is (or was) a /locations directory on both servers?
If the configuration is the same on both servers then you would get the same response, so there must be some difference. Or, there is a client-side caching issue.
So that if the user enters site.php/locations
I assume you meant site.com/locations - otherwise, the directives you posted have no hope of working, as they simply won't match.
The problem was due to a symbolically linked drive on Prod
I have a very small site with my .htaccess set up to allow urls like:
mysite.com/brand/login
mysite.com/brand/dashboard
mysite.com/controller
These all redirect to:
mysite.com/driver.php?___=brand/login
mysite.com/driver.php?___=brand/dashboard
mysite.com/driver.php?___=controller
I'm simply using one rewrite rule for this:
RewriteRule ^(([a-zA-Z0-9\-\_]+/?)*)$ index.php?___=$1 [L,QSA]
But anyway, it works great, until I have a page with the same name as a folder in my site.
// I navigate to:
mysite.com/javascript
// The site automatically provides a 301 redirect to
mysite.com/javascript/? ___=javascript
And now that ugly URL is showing up in my users' browsers. Is there any way to tell the site that I want to take care of all redirects myself?
EDIT: I determined this using fiddler. Another example:
Welp found the answer right after posting this. This line in .htaccess fixed the problem.
DirectorySlash Off
I then, of course, had to clear my browser cache to make it forget about the 301.
https://httpd.apache.org/docs/2.4/mod/mod_dir.html#directoryslash
The DirectorySlash directive determines whether mod_dir should fixup
URLs pointing to a directory or not.
Typically if a user requests a resource without a trailing slash,
which points to a directory, mod_dir redirects him to the same
resource, but with trailing slash
And a security warning:
Turning off the trailing slash redirect may result in an information
disclosure. Consider a situation where mod_autoindex is active
(Options +Indexes) and DirectoryIndex is set to a valid resource (say,
index.html) and there's no other special handler defined for that URL.
In this case a request with a trailing slash would show the index.html
file. But a request without trailing slash would list the directory
contents.
I'm using a shared hosting server with multiple domains, and my main .htaccess (inside the public_html/ directory) redirects all traffic to the server to my main domain (fine). The problem becomes now that I have Wordpress running on the main domain that the subfolder is being included in the %{REQUEST_URI} variable. So my .htaccess code:
RewriteRule .* https://%{HTTP_HOST}/index.php?%{REQUEST_URI} [L,R=301]
moves:
http://www.tonydiloreto.com/about/
to:
https://www.tonydiloreto.com/?/tonydiloreto/about/
(I put the ? in there so I could see what is being stored in the variable). My question is, how do I use regex to remove the additional /tonydiloreto from the %{REQUEST_URI} variable when Rewriting? If this were PHP I would do:
$new_url = str_replace("/tonydiloreto", "", %{REQUEST_URI});
If that makes sense. If you want to see what I'm talking about just go to my site now (tonydiloreto.com) and put anything after a trailing slash.
Thank you!
Not really sure what the issue here is... You simply specify a request rule which captures only part of the uri. There are millions of examples for this...
RewriteEngine on
RewriteRule ^/tonydiloreto/(.*)$ https://%{HTTP_HOST}/index.php?$1 [L,R=301]
This is documented, obviously: http://httpd.apache.org/docs/current/mod/mod_rewrite.html
Note: the above is the version for the host configuration. If you really have to use .htaccess style files, then you have to remove the leading slash (/) from the pattern, since such files work on relative URIs. .htaccess style files are notoriously error prone, hard to debug and really slow the server down.