Apache rewrite with fallback - apache

In our AngularJS project I have a set of HTML templates for reports that are currently part of the deployment but this means that whenever our client requests a change to one of the HTML templates I need to make update the complete application.
Is it possible to do the following for a request for these HTML templates:
Assume the request for assets/reports/report1.html.
Use the file <external-dir>/assets/reports/report1.html if that file exists. The external directory is not directly accessible from the internet.
Otherwise, use the initial request.
Most mod_rewrite solutions I could find seem to stop after the first rewrite.
Update: I've added the configuration changes below to the complete virtual host definition but the Alias seems to disrupt things:
<iFmODule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin webmaster#localhost
DocumentRoot /var/www/html
Alias /advisor/report /var/www/html/report
Alias /report-templates /opt/reports
#<Directory /var/www/html/report>
RewriteEngine On
# Preventing direct access to /report-templates
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^/?report-templates(/.*)?$ - [R=404,L]
# If the request was not already rewritten,
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# and the file do really exist in /advisor/report/assets/reports
RewriteCond %{REQUEST_FILENAME} -f
# rewrite the request from /advisor/report/assets/reports to /report-templates
RewriteRule ^/?advisor/report/assets/reports/(.*)$ /report-templates/$1 [L]
# If the file do not exist in /report-templates/
RewriteCond %{REQUEST_FILENAME} !-f
# rewrite the request back
RewriteRule ^/?report-templates/(.*)$ /advisor/report/assets/reports/$1 [L]
#</Directory>
LogLevel warn rewrite:trace8
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
</IfModule>
There are 2 AngularJS applications, the advisor portal located under /var/www/html/advisor and the reporting application located under /var/www/html/report. The latter is accessed through https:/example.com/advisor/report and is the reason for the Alias.
I changed the Alias to:
<Directory /var/www/html/advisor>
RewriteEngine On
RewriteRule ^/?advisor/report/(.*)$ /report/$1 [L]
</Directory>
This way the Alias should not interfere. After this I enabled the <Directory> directive for /var/www/html/report and removed the advisor/ part from the rewrite rules and conditions but I didn't see the rewrite happening in the logs.

I assume you have a directory called external-dir in your root (public_html) folder.
If you have full access to your server, this directory could be also somewhere else on the file system, but than you have to add a Alias in your Virtualhost configuration.
Alias "/external-dir" "/absolute/path/to/external-dir"
and the Apache Module mod_alias must be enabled:
sudo a2enmod alias
Now the main Part: This procedure will work for all existing files in the folder (or sub folder) /assets. First we check if the same file exist in /external-dir/assets and if so we serve it, if not we serve the original one. Its not possible to get a file from /external-dir/assets that do not exits in /assets. If you need more restrictions you can add them as your need it.
You can put this it in a .htaccess file in your root (public_html) folder or in your Virtualhost configuration, than I would use a Directory Direktive for your root directory
RewriteEngine On
# Preventing direct access to /external-dir
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^/?external-dir(/.*)?$ - [R=404,L]
# If the request was not already rewritten,
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# and the file do really exist in /assets
RewriteCond %{REQUEST_FILENAME} -f
# rewrite the request from /assets to /external-dir/assets/
RewriteRule ^/?assets/(.*)$ /external-dir/assets/$1 [L]
# If the file do not exist in /external-dir/assets/
RewriteCond %{REQUEST_FILENAME} !-f
# rewrite the request back
RewriteRule ^/?external-dir/assets/(.*)$ /assets/$1 [L]
I tested this positive on an Ubuntu 16.04.03 LTS Server with Apache 2.4.27.

Related

Stop virtualhost rewrite rules executing if a rule was matched in the global config

I have the following setup.
Apache running with a separate VirtualHost file for each site.
Each of these has their own set of rewrite rules, for http to https for example which is all running fine.
What we would like to happen is this, from the global config we need to be able to check if a request is for a particular subdirectory. If it is then we should allow this request to process as it should but at that point we do not want the individual virtual host file rewrite rules to kick in. Therefore allowing this directory to be served on non https connections and not be redirected to https.
I have set up the rewrite rules and can match on the directories and redirect to an external url if it matches from the global which shows its inheriting but if I try to just allow it through the virtual hosts rewrites kick in and it redirects.
I have tried using L and END but this did not work either.
Is there any way of achieving this without editing the virtual host files that are already configured?
Main httpd config entry
<Directory "/www">
Options Indexes Includes FollowSymLinks MultiViews
AllowOverride All
allow from all
Order allow,deny
Require all granted
RewriteEngine On
RewriteOptions InheritDownBefore
RewriteCond %{REQUEST_URI} ^/sub_directory/$ [NC]
RewriteRule ^(.*) $1 [L,END]
#RewriteRule ^(.*) - [L,END]
#RewriteRule ^(.*) http://www.google.com [L,END] # This does get triggered
</Directory>
sample virtual host file.
<VirtualHost *:80>
ServerName urlone.com
ServerAlias urltwo.com
DocumentRoot /www/
RewriteEngine On
# redirect to https
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*) https://urlone.com$1 [R=301,L]
</VirtualHost>
so if I visit urlone.com it should redirect to https://urlone.com but if I visit urlone.com/sub_directory it needs to not allow the redirect to https.
I hope this makes sense to someone and thanks in advance for any help.
In global httpd.conf:
RewriteEngine On
RewriteOptions InheritDownBefore
RewriteCond %{REQUEST_URI} ^/sub_directory$ [NC]
RewriteRule ^ - [E=PATH_MATCHED:true]
(if needed, you can add additional rules, or additional flags to the above rule)
In virtual_host.conf
RewriteCond %{ENV:PATH_MATCHED} !=true
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://urlone.com%{REQUEST_URI} [R=301,L]

Deploy a CakePHP website fails. An apache2 config issue?

I'm deploying a CakePHP (1.3.2) website for the first time. It was hosted on an older server. I received the project as a zip file. I managed to install it on my localhost and made the changes I needed.
Now I have to deploy it to a new server, but I face a problem.
The routing doesn't seem to work. I guess it's an .htaccess issue.
When I access the root folder, it redirects me to /login but then I have a 404:
The requested URL /login was not found on this server.
My 3 main .htaccess files (/, /app and /app/webroot) are the following. (CakePHP is installed at the root of my virtual host)
Root
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
</IfModule>
/app
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>
/app/webroot
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
mod_rewrite seems to be activated on my server as it responds with this when I try to add it:
Module rewrite already enabled
But when I try something simple like that on top of my root .htaccess, it doesn't do anything:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^.*$ test.php
</IfModule>
(I was following this guide: https://docs.bolt.cm/howto/making-sure-htaccess-works)
Among a lot of things, I also tried to add that to all my .htaccess:
Option Indexes
But it didn't help.
Here is my website conf file too:
<VirtualHost xx.x.xx.xx:80>
ServerAdmin xxx#company.com
ServerName xxx.company.com
DocumentRoot /var/www/xxx.company.com
DirectoryIndex index.html index.php
php_value error_log "/var/log/apache2/xxx.company.com-phperror.log"
php_flag register_globals off
<Directory "/var/www/xxx.company.com">
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
ErrorLog /var/log/apache2/xxx.company.com-error.log
CustomLog /var/log/apache2/xxx.company.com-access.log common
</VirtualHost>
(I added the Directory section that was not here in the first place)
After a lot of trials, I still haven't found anything that seems to solve my problem.
As I'm definitely not used to work on server side, It might be a simple thing that you will immediately spot. I hope so.
Thanks
I finally got it to work. Here are the two mistakes I made:
I had my document root set cake's root folder instead of the webroot folder. I added /app/webroot after DocumentRoot /var/www/xxx.company.com in my .conf file.
Also, I was using apache's reload function, which is actually not properly reloading. Using service apache2 restart instead does the job.

AllowOverride and RewriteCond/RewriteRule interactions

I'm trying to set up a site running on Apache 2.4.16 to redirect all www URLs to non-www URLs. I'm using HTML5 Boilerplate's Apache configs to do this (as well as everything else they provide).
https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess
This happens on line 380, seen below:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L]
</IfModule>
I'm using Include to add the whole file to my vhost config for the site, as well as an AllowOverride All for another .htaccess file at my doc root (same one that comes with Laravel 5):
production.vhost.conf (relevant part)
<Directory /var/www/hostname/production>
AllowOverride All
# Include H5BP server configs
Include server-configs-apache/dist/.htaccess
</Directory>
.htaccess (at doc root)
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Now, almost everything from H5BP's .htaccess was working, except for the redirect from www to non-www. After poking around I noticed that the redirect was only working when I'd remove AllowOverride All from the <Directory> block in the vhost. So the doc root .htaccess was somehow overriding the rewrite conditions.
I've actually already fixed my initial issue by moving the doc root .htaccess contents into the vhost file and removing the AllowOverride, but I'm more curious as to why this was happening; more specifically how AllowOverride interacts with RewriteCond and RewriteRule.
My hunch is that the .htaccess in my doc root was overriding the www to non-www redirect, but I'm not sure why that one specifically. For example, the http -> https redirect worked without issue (line 352 of H5BP, uncommented out in mine), it seemed to be just that one redirect. I didnt even think that those rules could be overridden since RewriteCond/RewriteRules feel unique to me.
If there are any, what are the rules that determine how an .htaccess can override a rewrite rule?
If there are any, what are the rules that determine how an .htaccess
can override a rewrite rule?
Conditions and rules don't dictate how .htaccess works. AllowOverride is what allows .htaccess usage. If you have AllowOverride All then .htaccess is allowed, if you have AllowOverride None, then it's not and it will be ignored. In 2.4 None is the default.
.htaccess is per directory so it will take precedence if it's located in a directory that has rules applied as long as .htaccess file usage is allowed there. Which is confgiured in the server config in VirtualHost or Directory directives.
Also using an include for .htaccess in a vhost is a very bad configuration. If you have access to the vhost file or the config, you should create another config and include it with the .htaccess contents.
You should not be using .htaccess files at all actually with access to the server config. See this apache recommendation on not using .htaccess.
https://httpd.apache.org/docs/2.4/howto/htaccess.html#when

Why is Apache Permanent Redirect removing the slash between the domain and the path?

I'm using Apache 2.4, and I set up two virtual directories. One requires SSL, and the other one redirects to it.
If a user attempts to visit https://www.derp.com/derp without /derp existing, they correctly get a 404. But when a user visits http://www.derp.com/derp, Apache incorrectly redirects the user to https://www.derp.comderp, removing the slash between the path and the domain name.
I have no idea what would be causing this.
The following is the setup of my Virtual Host.
<VirtualHost *:443>
ServerAdmin derp#derp.com
ServerName www.derp.com
ServerAlias derp.com
DocumentRoot "C:\Users\derp\Documents\Web Projects\derp"
SSLEngine on
SSLCertificateFile "C:\Apache24\certs\cert.cer"
SSLCertificateKeyFile "C:\Apache24\certs\key.key"
</VirtualHost>
<VirtualHost *:80>
ServerAdmin derp#derp.com
ServerName www.derp.com
ServerAlias derp.com
Redirect permanent / https://www.derp.com/
</VirtualHost>
<Directory "C:\Users\derp\Documents\Web Projects\derp">
Options Indexes FollowSymLinks Includes ExecCGI
AllowOverride All
Require all granted
SSLRequireSSL
</Directory>
Why would Apache be behaving this way?
Bonus Question: Should redirects be handled in my virtual host definition, or should it be handled in the .htaccess file in the web site's physical directory?
Edit:
I'm starting a Laravel project, and by default the public folder does contain a .htaccess file, so here's that guy:
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Edit Two:
I tried:
adding a slash at the end of the DirectoryRoot path
replacing the backslashes with forward slashes in the DirectoryRoot path
replacing the backslashes with double backslashes in the DirectoryRoot path
I also removed the .htaccess file from the directory completely.
It redirects correctly when you go from http://www.derp.com to https://www.derp.com. It's just when you specify a path and attempt https that it removes the slash between the domain and the path.
Edit Three:
I also attempted the following suggestion:
Redirect permanent / https://www.derp.com/
Try
RedirectMatch permanent /(.*) https://www.derp.com/$1
or
RedirectMatch permanent (.*) https://www.derp.com/$1
... and instead of redirecting to https://www.derp.comderp, it instead does not redirect, attempts and gives a 404 for http://www.derp.com/derp, but using Apache's 404, instead of throwing a Not Found Exception, as Laravel does without configuration.
Edit Four:
I have also tried:
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
In the .htaccess file and the behavior did not change at all.
I got it.
The issue did not lay with the rewriting at all, it was the SSLRequireSSL directive under my Directory definition that was causing the problem.
I simply removed this directive, refreshed the cache in all of my browsers, and the site then continued to work correctly. This was discovered through the process of elimination.
The documentation notes:
This directive forbids access unless HTTP over SSL (i.e. HTTPS) is enabled for the current connection. This is very handy inside the SSL-enabled virtual host or directories for defending against configuration errors that expose stuff that should be protected. When this directive is present all requests are denied which are not using SSL.
The emphasis is my own. SSLRequireSSL may have Apache only return a 403 or 404 if HTTP over SSL is not enabled, interfering with the Redirect rule. A rewrite rule such as the one in this answer on Server Fault may be a better alternative depending on your use case:
RewriteEngine On
RewriteCond %{SERVER_PORT} !443
RewriteRule ^(/(.*))?$ https://%{HTTP_HOST}/$1 [R=301,L]
My issue was related to browser caching.
I tried it in a different browser and it worked and then tried again in a private session in the first browser and it also worked.

Mod Rewrite: links being rewritten but not found

I am trying to use TYPO3 on a WAMP system but i'm having problems with the rewritten URLs.
I have installed the introduction package which has a "get-started" website. Everytime i try to access the website through one of these:
localhost/typo3
localhost/typo3/index.php
localhost/typo3/index.php/get-started
the url becomes localhost/typo3/get-started
Which is okay and it means mod_rewrite is on and working. The problem is that i can't see the site localhost/typo3/get-started and i have an "Object not found" page instead.
I have the same issue on the same machine with Symfony 1.4 but i never cared about that because on Symfony i can use the frontend_dev.php page to access the site (on my production environment rewritten URLs work fine instead).
This is the httpd.conf entry for the TYPO3 directory:
Alias /typo3 "C:\workspace\typo3"
<Directory "C:\workspace\typo3">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
Allow from all
</Directory>
And this is the .htaccess file (which was already inside the TYPO3 package and i haven't modified), i have removed the non related parts
### Begin: Settings for mod_rewrite ###
# You need rewriting, if you use a URL-Rewriting extension (RealURL, CoolUri, SimulateStatic).
<IfModule mod_rewrite.c>
# Enable URL rewriting
RewriteEngine On
# Change this path, if your TYPO3 installation is located in a subdirectory of the website root.
#RewriteBase /
# Rule for versioned static files, configured through:
# - $TYPO3_CONF_VARS['BE']['versionNumberInFilename']
# - $TYPO3_CONF_VARS['FE']['versionNumberInFilename']
# IMPORTANT: This rule has to be the very first RewriteCond in order to work!
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gzip)$ $1.$3 [L]
# Stop rewrite processing, if we are in the typo3/ directory.
# For httpd.conf, use this line instead of the next one:
# RewriteRule ^/TYPO3root/(typo3/|t3lib/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]
RewriteRule ^(typo3/|t3lib/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]
# Redirect http://example.com/typo3 to http://example.com/typo3/index_re.php and stop the rewrite processing.
# For httpd.conf, use this line instead of the next one:
# RewriteRule ^/TYPO3root/typo3$ /TYPO3root/typo3/index.php [L]
RewriteRule ^typo3$ typo3/index_re.php [L]
# If the file/symlink/directory does not exist => Redirect to index.php.
# For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
# Main URL rewriting.
# For httpd.conf, use this line instead of the next one:
# RewriteRule .* /TYPO3root/index.php [L]
RewriteRule .* index.php [L]
</IfModule>
### End: Settings for mod_rewrite ###
Solved, the problem was on the easyphp settings because i wasn't using its DocumentRoot (but just using aliases), changing the DocumentRoot to the one where i keep my projects solved it
You should change the #RewriteBase / to RewriteBase /typo3/ as described inside the .htaccess file.