Using mod_write for cleanurls with Lets Encrypt - apache

I have enabled Let's Encrypt on a server running Apache on Ubuntu 14.04 and used the auto option to re-direct all http requests to https. This is working fine.
However, I now want to use mod_rewrite to use cleanurls on my site - all I need to do is remove the .php extension from all filenames. (e.g. https://example.com/contact routes to https://example.com/contact.php)
I have tried adding the following rewrite rule to the .htaccess file:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php
This configuration works fine on my localhost setup (without SSL) but does not work on the instance running Lets Encrypt.
I have tested that the .htaccess is working by adding this rule which works as expected (redirecting all www requests to the root domain)
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
I suspect that there may be some conflict between the Lets Encrypt auto setup option and my mod_rewrite rule but I am stuck as to how to make them both work together.
Any help would be much appreciated.

Disable MultiViews in .htaccess:
Options -MultiViews
MultiViews (part of mod_negotiation) is likely resulting in a conflict. This does something very similar to what you are trying to achieve using mod_rewrite. With MultiViews enabled (possibly enabled in the server config, although the default is disabled), a request for /filename, will result in Apache looking for a file that matches (that would return the appropriate mime-type) by stepping through the files in that directory (essentially trying various extensions where the basename matches).
I have checked what REQUEST_FILENAME is returning - it is the path to the filename (e.g. [REQUEST_FILENAME] => /var/www/sitename/public_html/output.php)
Yeah, that's the problem. MultiViews has already "fixed" the URL (output to output.php) before mod_rewrite has been able to do its thing.

Related

How to avoid the need of typing .php on the url?

I'm on MacOs Big Sur, using Apache and PHP. What I want is: not needing to put .php on the end of my files to load it.
For instance, instead of typing this on the URL:
127.0.0.1/public_html/home.php
I want just to type
127.0.0.1/public_html/home
To achieve this, I'm using this code in .htaccess:
RewriteEngine On
Options -Indexes
DirectoryIndex home.php index.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
The code above works on my hosting, but for some reason, it does not work on my development machine. Instead, a get a 404 error.
The .htaccess file with the code is on the root of public_html folder.
What am I missing?
By typing some "nonsense" at the top of the .htaccess file and not getting an error (ordinarily you would get a 500 Internal Server Error) it would seem that .htaccess overrides were not enabled on the server. So, .htaccess files were effectively disabled - which they are by default on Apache 2.4.
To enable .htaccess overrides (to allow .htaccess to override the server config) you need to set the AllowOverride directive in the appropriate <Directory> container in the server config (or <VirtualHost> container). The default on Apache 2.4 is AllowOverride None.
With the directives as posted you would need a minimum of:
AllowOverride FileInfo Indexes Options
FileInfo for mod_rewrite, Indexes for DirectoryIndex and Options for Options and related directives.
Although it is common (and easier) to just set:
AllowOverride All
Reference:
https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
These directives are not strictly correct. Whilst they may work OK for the URLs you are testing, they would result in a rewrite-loop (500 error response) if you simply append a slash to your URLs (and there is no directory by that name), eg. /home/ (or /home/<anything>). This is because your condition that tests for the presence of the .php file is not necessarily the same as the URL-path you are rewriting to. See my answer to the following question on ServerFault for a thorough explanation of this issue: https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-error
Also, there's no need to check that the request does not map to a directory to then check if the request + .php extension maps to a file. If the request maps to a file then it can not also be a directory, so if the 2nd condition is true, the 1st condition must also be true and is therefore superfluous.
And there's no need to backslash-escape literal dots in the RewriteCond TestString - this is an "ordinary" string, not a regex.
So, these directives should be written like this instead:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule (.+) $1.php [L]
(RewriteBase should not be used here.)
You can further optimise this by excluding requests that already contain what looks like a file extension (assuming your URLs that need rewriting do not contain a dot near the end of the URL-path). For example:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.php [L]
(With this 2nd version, it does not matter if RewriteBase is set - it is not used.)
DirectoryIndex home.php index.php
You gave an example URL of /public_html/home (to which .php is appended). However, this DirectoryIndex directive allows home.php to also be served when simply requesting the directory /public_html/. It should be one or the other, not both.

How Best to Change the Symfony Front-End Controller

I created a new Symfony project, without using the installer, according to the instructions here: http://symfony.com/doc/current/book/installation.html#creating-symfony-applications-without-the-installer.
I accepted all the defaults, including the demo bundle, AcmeWebBundle.
I configured an Apache virtual host doc-rooted at the new project and restarted the web server. I would like to access the application via "http://virtualhost/" (ie, without explicitly calling the front-end controller app.php or app_dev.php).
Installed with the project was a .htaccess file that sets the DirectoryIndex to app.php. I would like to use the developer environment for development, but (long story short) it seems the only way to do this is by manually editing the .htaccess file - both the DirectoryIndex setting, plus a few mod_rewrite directives.
Is there a simpler way to switch between environments/controllers without having to edit the .htaccess file? For bonus points, I had to figure this out by trial and error - was this explained in the documentation someplace that I missed?
Thanks, and have a nice day.
You need this one:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{SERVER_NAME} \.dev$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app_dev.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
</IfModule>
If you use host that ends on .dev you will work with dev-environment.
For example you can use virtualhost as prod-env host and virtualhost.dev as dev-env host.

.htaccess rewrite url for missing reources

I would like to rewrite files that don't exist to a php handler. I am currently using this .htaccess file, but it doesn't work as I'd like:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /app/index.php?_url=/$1
When I have no files, it works; it redirects correctly for resource that does not exist to my app, and I am able to capture it.
When I have exactly matching file (e.g.: test.txt and I request /test.txt); it loads test.txt correctly.
However, when I have a partial match (e.g.: test.txt exists, but I request /test); it does not redirect at all. In fact, it gives me the standard Apache 404. I want this to actually rewrite to my app, so I can deal with the request in a different manner.
I'm using pretty much default apache 2.2.22 from Debian. Is there some configuration I am missing, or is this the intended behaviour of the rewrite? Is there a way to achieve what I want?
I think you have MultiViews enabled in your Apache by default. Try adding this line on top of your .htaccess:
Options -MultiViews
With MultiViews Apache does its own rewrites and that usually conflicts with mod_rewrite

My htaccess RewriteRule works on localhost/mysite but not at mysite.com on a 1&1 shared host

Solved:
unfortunately, the solution is not a satisfying one. This morning, when trying #Wige's Suggestion, I found, to my suprise, that the Expected values WERE infact sent to the page as a GET query. Apparently, 1&1 (who I know have been making changes to their environment this last couple weeks), did something behind the scenes which magically fixed my problem, and now all of my previously unworking code is working as originally expected.
New info: The Apache version of the production server is 1.3.34 vs 2.2.21 on my localhost.
I'm having trouble figuring out why my RewriteRule is not working properly in production.
RewriteRule ^page/pretty-url/(.*)$ page.php?query=$1 [L]
In my local testing environment (localhost/mysite/page/pretty-url/{...}) it works fine, but on mysite.com/page/pretty-url/{...} it doesn't work properly. It loads page.php as expected but apparently the ?query=$1 piece is ignored ($_GET is empty)
I imagine that the problem is somehow related to the server configuration. I'm on a 1&1 shared hosting account with no httpd.conf access.
What that RewriteRule does (or should do):
I want urls like
*example.com/page/pretty-url/{{info_for_dynamic_content}}
to be rewritten to
*/page.php?query={{info_for_dynamic_content}}
So I can access info_for_dynamic_content
within php as $_GET['query']
The full .htaccess file for reference:
AddHandler x-mapp-php6 .php
DirectoryIndex index.php
ErrorDocument 404 /index.php
Options +FollowSymLinks
# per #Jacques Chester's suggestion
Options -MultiViews
RewriteEngine on
RewriteBase /
# the rule in question
RewriteRule ^page/pretty-url/(.*)$ page.php?query=$1 [L]
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php !-f
RewriteRule (.*) /index.php [L]
Most likely, your host is storing the variables somewhere else. I would add a call to phpinfo(); into your script and go through the environment variables there to see if you can find the values that should have been in get.
Seems to be connected to 1&1's hosting environment.
See this question, in particular, this answer.
Basically it appears that 1&1 enable "MultiViews". By adding
Options -MultiViews
You disable that setting for your website and according to various reports, this resolves the issue.
I struggled with RewriteRule issues on a 1&1 / 1and1 / IONOS shared server for WEEKS and eventually I found the perfect setup for a 1 and 1 shared server, start your .htaccess file like this
Options -MultiViews
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
I hope this helps someone as 1&1 are useless when it comes to htaccess support

.htaccess require SSL for a particular URL

I want to force Apache to use HTTPS for a particular URL in the following form:
https://www.example.com/signup/*
so
if someone goes to any of the following example URLs directly, Apache will forward the URL over to the HTTPS equivalent site.
e.g.
http://www.example.com/signup --> https://www.example.com/signup
http://www.example.com/signup/basic+plan --> https://www.example.com/signup/basic+plan
http://www.example.com/signup/premium --> https://www.example.com/signup/premium
Anyone know how?
Thanks in advance
Thank Murat,
Yours almost worked but figured out how to get it to exactly work.
The following is what works:
RewriteCond %{SERVER_PORT} 80
RewriteCond %{REQUEST_URI} ^/somefolder/?
RewriteRule ^(.*)$ https://www.domain.com/$1 [R,L]
Notice that I didn't include somefolder in the www.domain.com rewriterule
I think this was what i used:
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteCond %{REQUEST_URI} ^/somefolder/?
RewriteRule ^(.*)$ https://www.domain.com/somefolder/$1 [R,L]
(from here)
You can use the Redirect directive:
Redirect 301 /signup https://www.example.com/signup
This will automatically preserve anything following /signup in the URL. Be sure to configure this directive only on your non-SSL site, or it might get into a recursive loop!
You should take a look at mod_rewrite documentation
I used the following to require the checkout section of a website to require SSL:
<Directory "/var/www/html">
RewriteEngine on
Options +FollowSymLinks
Order allow,deny
Allow from all
RewriteCond %{SERVER_PORT} !^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule ^checkout(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>
So for example, hitting http://www.example.com/checkout redirects to https://www.example.com/checkout
The rule will skip file extensions that are typically included within a page so that you don't get mixed content warnings. You should add to this list as necessary.
If you want multiple pages change the RewriteRule to something like:
RewriteRule ^(checkout|login)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
Of course, the directory should match the actual path on your server. This page may also help with some more information for your specific needs: http://www.whoopis.com/howtos/apache-rewrite.html
I'm using this on a website that runs Plesk 8.6 but that shouldn't matter. This is in my vhost.conf file which is like putting it in your httpd.conf file. I'm not sure if you'd need to adjust anything to use it in a .htaccess file but I doubt it. If adding to a conf file don't forget to restart apache to reload the configuration.
If you are like me and want to use SSL only on particular pages then you also want a rewrite rule that sends you back to regular http for the rest. You can use the following for the reverse effect:
RewriteCond %{SERVER_PORT} ^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule !^(checkout|login)(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [L,R]
If you are using Plesk like I am keep in mind that all non-SSL traffic uses the vhost.conf file but all SSL traffic uses the vhost_ssl.conf file. That means your first rewrite rule to require SSL would go in the vhost.conf file but the second rule to force back to non-SSL will have to go in the vhost_ssl file. If you are using httpd.conf or .htaccess I think you can put them both in the same place.
I've also posted this tutorial on my blog: Apache rewrite rules to force secure/non-secure pages.
You can do this with mod_rewrite -
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/signup https://example.com/signup
RewriteRule ^/signup/(.*)$ https://example.com/signup/$1
Should work, though I haven't tested it.
-- edit --
Correction, I just tried this on one of my servers, and it works fine for me. You may want to doublecheck your mod_rewrite configuration. Also, if you're using .htaccess, you'll want to make sure overrides are allowed for that directory.
As a side note, this assumes your SSL traffic is coming over port 443. If it isn't, you'll need to adjust the rewrite condition accordingly.
.htaccess files are normally placed in a scope with Options -FollowSymLinks, which blocks Rewrite rules. This is often a security rule.
So a more trivial thing is often needed like this one:
<If "%{HTTPS} != 'on'">
Redirect 301 /your/path https://www.example.com/your/path
</If>
This is a small enhancement to the answer of Greg Hewgill.