Symfony 2 404 Custom page for missing resource - .htaccess

So, I have been trying to add a 404 controller in case an image is missing and I need to go and get it.
Initially, I tried a NotFoundHttpException/ResourceNotFoundException listener which loaded a class to deal with it. This worked great, if it was a Symfony2 routing issue.
Problem is, it isn't. It is a /web/bundles/mysite/images/missingimage.jpg 404, which is handled by Apache it seems.
So with .htaccess I tried:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .? - [L]
RewriteRule .? %{ENV:BASE}/app.php [L]
ErrorDocument 404 /web/404.txt
</IfModule>
Just to see if I could get that to work, but I get the feeling it wouldn't and it didn't.
So how can I get my setup to forward to a Symfony2 page when it encounters a 404 within the /web folder.
I am guessing it should be .htaccess, but I am not having much look.
In the end, I want it to go to:
/web/app.php/FourZeroFourHandler/web/bundles/mysite/images/missingimage.jpg
or:
/web/app_dev.php/FourZeroFourHandler/web/bundles/mysite/images/missingimage.jpg
Depending on whether I am in prod or dev.
And ideas?

You have two options.
First one is to route all images via your Symfony2 app. The LiipImagineBundle does this in order to generate thumbnails. Like that you could redirect all 404 to your custom error page. Downside would be that even for a static image the whole Symfony2 kernel needs to be booted. That's time and resource expensive.
The second option is to redirect from Apache2 to a route that is unknown or to a route that throws a 404 / HttpNotFoundException:
/*
* #Route('/404', name="not_found")
*/
public function notFoundAction()
{
return new HttpNotFoundException();
}
That gives you more control over the error message since you can render a twig template or log an error message.
You are right. I also had some trouble with the custom controller when I inserted ErrorDocument in my .htaccess. The solution is to edit your vhost instead. Go to /etc/apache2/sites-available and select the file (by default it's called default) where your virtual host ist stored. Replace the AllowOverride option in the <Directory>. And replace it with these lines:
AllowOverride FileInfo
ErrorDocument 404 /not_found.html
Solution was found here. Don't forget to reload your apache configuration:
sudo service apache2 reload

Related

Proper .htaccess settings for Next.js SSG catch all routes

I've build a website, using Next.js and SSG - Static Site Generation.
I'm serving the website on an Apache server and, therefore, because of this answer, I'm using the following .htaccess configuration:
# Disable directory indexes and MultiViews
Options -Indexes -MultiViews
# Prevent mod_dir appending a slash to directory requests
DirectorySlash Off
# Rewrite /foo to /foo.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.html [L]
# Otherwise, rewrite /foo to /foo/index.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}/index.html [L]
# Handling 404
ErrorDocument 404 /my-custom-404.html
And it's been working perfectly.
But recently, there was a need for an admin page, the usual CRUD. I've developed it without SSG in mind (because there were actually no need for it), created my own custom router and used Next.js's catch all routes to handle all content of this admin page, which I called dashboard.
So, on my built website, I have a whole bunch of page.html files, and a /dashboard/[[...dashboard]].html file, which is the CRUD itself (all subpages and contents are imported dynamically) - and, therefore, every single link starting with /dashboard will be handled via that file.
So, the problem is: navigating through the links on the website and into the dashboard, everything works perfectly. But if I try to directly access any link within /dashboard (including itself, and subroutes: /dashboard/users, for instance), it simply does not work. With the provided .htaccess configuration, it goes straight to 404.
I've tried a few different configurations added to .htaccess, but none have worked so far (either it still goes straight to 404, or the browser shows the error ERR_TOO_MANY_REDIRECTS):
RewriteCond %{DOCUMENT_ROOT}/dashboard/$1
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
This one almost worked... but the dashboard itself does not do anything (no subpages are ever rendered):
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
Important note: this whole thing works perfectly on the local dev server.
If anyone has any tip on how I can sort this thing out, I would really appreciate it!
I've managed to solve it, at least on an acceptable level.
As I mentioned on the question, this was almost working (no sub page was ever rendered, but at least the browser was correctly redirecting to the dashboard):
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
No page was ever rendered, because I was using Next.js's withRouter/useRouter to inform my custom router of the current route (via router.asPath), and when the above rule as met, router.asPath was set to dashboard/[[...dashboard]].html.
So, I wrote a small test to redirect any access with that asPath to my entrypoint. Basically:
if(router.asPath.contains('[[...dashboard]]')
router.push('/dashboard')
But, another problem arouse...
The first problem was solved: trying to access any dashboard routes directly was working, but navigating through the website (links) was not. After an arduous and long process of trial and error, I found out that my previous .htaccess rule was preventing my client-side code to request my [[...dashboard]].html file.
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteCond %{REQUEST_URI} !(\[\[\.\.\.dashboard\]\])
RewriteRule (.*) /dashboard/[[...dashboard]].html?p=$1 [L]
Basically:
If my request URI contains 'dashboard'
AND if that same request does not contain [[...dashboard]]
THEN I rewrite it to the dashboard file
Hope this might help someone someday!

Trying to put website into Maintenance Mode (302) - 'Too many redirects' htaccess issue

I'm trying to put my webpage into Maintenance Mode by using htaccess to redirect any page that begins with (domain name) to a maintenance.php file within a folder inside the root.
I got this to work on localhost with no issues, but it just won't work when I put it on my web host server. It keeps saying there are too many redirects (there's an infinite loop going on).
# MAINTENANCE-PAGE REDIRECT
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !^100\.184\.54\.96
RewriteCond %{REQUEST_URI} !/maintenance/maintenance.php$ [NC]
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif) [NC]
RewriteRule .* /maintenance/maintenance.php [R=302,L]
</IfModule>
I tried plenty of the answers given to other questions such as
.htaccess error - ERR_TOO_MANY_REDIRECTS
htaccess maintenance page redirect results in "too many redirects" error
...among others. The same error keeps coming. I have another domain (domain-1) redirecting to the current webpage (domain-2), tried turning that off to see if it works, nope.
After following a ton of suggestions and styles from around the net, I finally came to a solution that worked for this issue.
To redirect all pages and sub-directories for your domain name to a maintenance page, create two files:
maintenance.html (maintenance page)
maintenance.enable (empty file)
Use the following code in your .htaccess file:
RewriteEngine On
RewriteCond %{REMOTE_ADDR} !^105\.228\.123\.16
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
RewriteCond %{DOCUMENT_ROOT}/maintenance.enable -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /maintenance.html [R=503,L]
ErrorDocument 503 /maintenance.html
Header Set Cache-Control "max-age=0, no-store"
Be sure to place the 2 files in the same directory as your index page.
That's the solution that worked in my case. I'm yet to try it out with external resources (css/js files and images) but I think it shouldn't take more than some tweaking the above code. Hope it helps someone else too.
EDIT
For external resources and styling just add this line:
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif|css|js|ico)
Be sure to add all of the relevant directories (containing the stylesheets and scripts) in the same directory as the maintenance.html page.
I could be wrong but it seems like a bad idea to use this in conjunction with Header Set Cache-Control "max-age=0, no-store" if you're going to keep the maintenance page up for a while. I leave that for the experts though :-)
My maintenance page is a fancy countdown page.
This is actually part of the problem. Your "fancy" page contains links to numerous CSS and JS files (and the favicon.ico file) - 17 files in total - your .htaccess redirect will redirect these requests as well (all to your maintenance.php page - which will trigger further redirects etc.). You'll need to make additional exceptions for these URL/file extensions. For example:
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !^100\.184\.54\.96
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif|css|js|ico)$
RewriteRule !maintenance\.php$ /maintenance/maintenance.php [R=302,L]
The <IfModule mod_rewrite.c> wrapper is not required (you know your server).
The NC flag is not required unless you really do have mixed case extensions.
I realise this isn't a normal "site down for maintenance" type page, however, maintenance pages should ideally link to as few external resources as possible. To avoid issues like the above, but also you don't want to be in a situation where the maintenance page itself cannot be displayed because the site is down for maintenance!

Laravel project in subfolder of public_html gives trouble with .htaccess redirect

I've set up my VPS server and am trying to set up a Laravel project.
I have a /public_html/ folder with an index.html that's shown when visiting the website URL.
I have a laravel project in: /public_html/project_one/
The original index.php is located in /public_html/project_one/public/index.php
I want the website to show to Laravel project_one by default, but since yesterday I literally googled this for 4 hours, I tried every solution on stackoverflow, nothing worked... I think I've tried over 10 versions of .htaccess I could find.
The question:
Does anyone know how to setup the .htaccess inside /public_html/ so it redirects to my Laravel project?
I though I needed to upload my Laravel folder in: /public_html/project_one/ but is there a better location?
I want the main url to show the Laravel project. So www.mainurl.com is Laravel's project_one/public/index.php file. I do not want /project_one/ to be written in the URL!
Just out of curiosity, if I now visit: www.mainurl.com/project_one I get a 403 forbidden.
Failed solutions
In /public_html/ I tried to add .htaccess with:
DirectoryIndex index.php
#Redirect to /project_one/public if you haven't already and the IP is okay
RewriteCond %{REMOTE_ADDR} ^1\.2\.3\.4*
RewriteCond %{REQUEST_URI} !^/project_one/public
RewriteRule ^(/lucaphoto)?(.+) /project_one/public$2 [L]
#if IP does not match and you ARE in the folder, then Redirect to root
RewriteCond %{REMOTE_ADDR} !^1\.2\.3\.4*
RewriteCond %{REQUEST_URI} ^/project_one/public
RewriteRule .? / [R=301,L]
This gives me something really strange:
On Chrome: it redirects me to www.mainurl.com/project_one -> And then gives me a 403 forbidden error.**
On safari: it redirects me nowhere but gives me this error:
Forbidden. You don't have permission to access / on this server.
Additionally, a 404 Not Found error was encountered while trying to
use an ErrorDocument to handle the request.
Laravel's original .htaccess File
By the way, Laravel has an original .htaccess file at /project_one/public/.htaccess. And it looks like this:
<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]
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
The actual laravel application ( anything but the public folder ) is not supposed to be public and therefore should not be placed inside the public ( public_html ) folder.
I actually tried severel things to get something similar to your setup running. My conclusion was - that only changing the .htaccess file will net e enough to run laravel inside a public folder.
The most easy solution to quickly setup a laravel application is to use a subdomain which directly points to the public folder.
www.mainurl.com /* points to .../public_html/ */
laravel.mainurl.com /* points to .../laravel/public/ OR ( but not good ) */
/* points to .../public_html/my-project/public/ */
What you actually will need to do then is to change the root of your domain / subdomain to point into another folder - no changes to .htaccess are needed then.
Update
If your site is www.example.com and you want your laravel app to show when opening this site you have two options.
Put the content of laravels public folder to public html and change the references to fit the public_html instead of public folder.
If your Filestructure on the server is like .../var/stuff/.../public_html/ then upload everything to ../var/stuff/my-project and then go to your hosters interface and make an internal redirect to the ../var/stuff/my-project/public folder and everything will be fine without chaning anything in your laravel code. ( anything but .env )
So the actual problem is that your domain points to the folder public_html, instead of the public folder of your app. However please dont put your app into public_html directly, put it parallell to it and change where the domain points to.
if you give us some information about your hoster we might can provide a guide

301 Redirect entire site to new directory except for index.html landing page

The wordpress site I'm working on got moved into a subdirectory therefore all the links from other sites don't work anymore. I implemented a 301 redirect with .htaccess which is great because it fixes that problem BUT the old root directory now has an index.html that has landing page my client absolutely wants to be seen.
So, how can I set up my .htaccess to redirect all traffic into the sub directory (to fix the incoming links) EXCEPT the index.html in the root directory because it has the landing page.
I don't know how htaccess works well but this is what I have right now.
Order deny,allow
ErrorDocument 404 /404.php
DirectoryIndex index.php index.html
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/$ [OR]
RewriteRule ^.*$ http://example.com/portal/$0 [R=301,L]
Thanks!!
Edit for clarification:
Right now EVERYTHING redirects from the root to the subdirectory. I want everything to redirect except for index.html in the root directory. If the user requests just the domain name (http://example.com) without specifying a page, I also want him/her to be served up the index.html page in the root directory.
The following code does what you are asking for: "if the request does not match either index.php or index.html or "/" (i.e. nothing) (and the match is not case sensitive) then serve up the alternate location"
Order deny,allow
ErrorDocument 404 /404.php
DirectoryIndex index.php index.html
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/index\.(php|html) [NC]
RewriteCond %{REQUEST_URI} !^/$ {NC]
RewriteRule ^.*$ http://example.com/portal/$0 [R=301,L]
I have tested this using the excellent online testing tool http://htaccess.madewithlove.be
Using the following test cases:
http://example.com -- no rewrite, second condition not met
http://example.com/ -- ditto
http://example.com/index.html -- first condition not met
http://example.com/index.php -- first condition not met
http://example.com/some/page.html -- rewritten as http://example.com/portal/some/page.html
EDIT You said that this still didn't work quite as expected; so I brought out the big guns. By turning on "maximum logging" of everything that the rewrite engine does with the directives
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 9
(pick any path you want, obviously), then looking at the end of the logfile in a terminal window with
tail -f /var/log/apache2/rewrite.log
You can quickly see where things are not working quite right. A bit of fiddling led me to the following code. It says "if the requested URI is just /index.html or /index.php, or if it starts with /portal, or if it is blank, then don't redirect.
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/index\.(php|html) [NC]
RewriteCond %{REQUEST_URI} !^/portal.*$ [NC]
RewriteCond %{REQUEST_URI} !^/$ [NC]
RewriteRule ^(.*)$ http://example.com/portal$0 [R=301,L]
The test cases worked for me - see if they work for you!
NOTE: I made these changes in the httpd.conf file, not in the .htaccess file of the root directory. You need to be careful to make it so the .htaccess file in the root directory is even read - the default Apache configuration has an Override none for that directory, so some extra work is needed. By putting this configuration change in the httpd.conf file (and issuing a sudo apachectl restart command) you avoid the difficulty. Depending on who is hosting your website, and what control you have, that may not be an option for you. There may come a point where the experts for this problem can be found on superuser.com rather than SO... but I'm hopeful this does the trick for you.

How can I get non existant files mapped correctly in .htaccess?

Duplicate:
How to rewrite non existant files to
‘default’ files?
(.htaccess)
How would I "rewrite" to a location if a file doesn't exist? I don't want to use a 404 redirect, but an actual rewrite.
So for example, let's say it is a directory with images. If the image isn't found, then it rewrites to a default image?
I.e.,
images/1.jpg
images/2.jpg
images/default.jpg
if someone tried to access "website.com/images/3.jpg",
since that doesn't exist, I want it to go to:
"website.com/images/default.jpg"
This was a previous "posted" solution, but didn't quite work:
RewriteCond %{REQUEST_FILENAME} !-f [NC]
RewriteRule /images/.* /images/error.jpg [L]
It still doesn't "get" the right image (just goes as a regular 404 request).
RewriteCond %{REQUEST_FILENAME} !-f [NC]
RewriteRule ^images/.* /images/error.jpg [L]
Obviously this only redirects if missing file is under /images/... but you can easily modify it for your own needs
Well, your previous posted solution is on the right track, but there's some slight craziness with it. Try this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule images/.* /images/default.jpg [L]
You should better send a 404 status code if the file really doesn’t exist rather than just a substitute with a status code other than 404. Otherwise the URL will be handled as valid.
So in your case I recommend you to set the ErrorDocument of the images directory to your default image:
<Directory "/path/to/your/images/">
ErrorDocument 404 /images/default.jpg
</Directory>
But note that the <Directory> block is only available in the server or virtual host configuration context and not the per-directory context (thus .htaccess).
If you cannot use the above, you could use a custom script as your custom error document to check what URL has been requested (see Request_URI environment variable) and send the default image if necessary. The ErrorDocument directive then should look like this:
ErrorDocument 404 /your-error-404.script
re-write your 404 document for your images folder:
(In your .htaccess file in your images folder)
ErrorDocument 404 default.jpg

Resources