Vue.js Router: history mode and AWS S3 (RoutingRules) - amazon-s3

I have a Vue.js application up and running with Amazon S3 and Cloudflare.
When I open the index and browse to /dashboard, everything works fine. But when I open a route like dashboard directly in a new tab or refresh the page, I get the following error back from S3:
404 Not Found
Code: NoSuchKey
Message: The specified key does not exist.
Key: unternehmen
RequestId: 6514F8A1F4C29235
HostId: +BVrPLJSGdzSYogzWZ4GMBXkgkdSJWRVJVhcSs4EI/lmMUR422aCtCxpBGU6AMe5VkS1UbEn/Lc=
Just read that the problem is Vue.js history mode: https://router.vuejs.org/de/essentials/history-mode.html
I would like to solve the problem with a routing rule in my Amazon S3 Bucket. How would the Apache RewriteRule look for S3?
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
Tried the following but it does not work:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName>domain.com</HostName>
<ReplaceKeyWith>index.html</ReplaceKeyWith>
</Redirect>
</RoutingRule>
</RoutingRules>
If I do it like that I just get my Header and Footer rendered but nothing more.
Thank you!

I know this answer comes late but in case someone else is looking for another way to solve this in cloudfront, there's no need to create custom pages on s3 redirecting users when pages are not found. Instead of doing it, custom error responses can be created in cloudfront for the distribution.
This will always redirect to /index.html in case a file is not found and that will make the app route trigger.

If you use static hosting on AWS S3 and this is SPA app (React, Vue, Angular etc) you should set index.html as error page:

I'm hosting a static PWA made in Vue on S3 using "Static website hosting" and it works as expected.
What I did was quite simple - I've added this:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>403</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<ReplaceKeyWith></ReplaceKeyWith>
</Redirect>
</RoutingRule>
</RoutingRules>
To the Properties of the S3 bucket. See image below:
For my S3 bucket, everytime you try to access a file that doesn't exist you will receive a 403 (forbidden) instead of a 404. That is why I've changed the HttpErrorCodeReturnedEquals to 403. I've also replaced the ReplaceKeyWith with an empty string as "index.html" as not triggering the correct route.
I hope it helps.
Cheers,
Alex

I think this problem has two components:
1.
If a request is made directly (outside of the Javascript App) to a sub path such as /jobs then S3 returns a 404, because the path/object doesn't exist. The simplest way to fix this is from within S3 itself, where you redirect all error pages back to index.html.
However this doesn't work from a CDN such as Cloudfront, and presumably Cloudflare.
A good trick is to use files inside S3 that redirect users like this:
jobs/ -> /index.html
jobs -> /index.html
For example if someone makes a request to site/ they will get the following html file:
"redirect":"<html><head><meta http-equiv=\"refresh\" content=\"0; url=http://example.com/site/index.html\" /></head>
<body>Redirecting to Home</body></html>"
Secondly...
If I do it like that I just get my Header and Footer rendered but
nothing more.
This is a problem I've had where the router-view doesn't initialise properly, even though the component that contains the router-view has loaded.
What I have done for now is redirect my router when the main "App" component is created:
created () {
console.log('route', this.$route.path)
this.$router.replace(this.$route.query.redirect || '/')
}
This has the added bonus of removing the index.html from your path (Which was put there by your new redirections) whilst forcing your router-view to render...
My #app component is the parent component, with a navbar, footer, and the router-view that renders all of the sub pages/components.

One way to make this work in with routing in history mode and without using CloudFront is to create the files that your URIs would point to. So let's say you use index.html as your entry point and have only one other page which you give path page2.html.
Then you should adapt your build and deploy process to do the following:
Build your source into a production distribution (E.g. using npm run build)
Make sure that for your router links you create a file per path that is a copy of the original file. In case of the example I mention it would be creating a copy of 'dist/index.html' to 'dist/page2.html'
Copy the dist folder contents to your S3 bucket
Note that the order of step 1 and 2 is important.
This can be easily done in a script that publishes your website.
I have just used this approach with the result I wanted; allowing refreshinag a page and opening links in a new tab.
Drawbacks I see is:
- overhead of storing the copies of the files
- have to make sure if you add a page not to forget to update your build and deploy scripts.

The selected answer (403 and 404) did not work for me.
It was only after I added a similar 400 rule did my VUE app function in history mode.
for anyone using an AWS SAM template:
CloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
...
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: /index.html
- ErrorCode: 400
ResponseCode: 200
ResponsePagePath: /index.html

Related

How to redirect 404 errors (and 403) to index.html with a 200 response

I am building a static website that uses JS to parse a URL in order to work out what to display.
I need every URL to actually open index.html where the JS can pull apart the path and act accordingly.
For example http://my.site/action/params will be parsed as an action with some parameters params.
Background, this will be served from AWS S3 via CloudFront using custom error redirection - and this works fine on AWS.
I am, however, trying to build a dev environment under Ubuntu running apache and want to emulate the redirection locally.
I have found a couple of pages that come close, but not quite.
This page shows how to do the redirect to a custom error page on the server housed in a file called "404". As 404 is the actual error response code, the example looks a bit confusing and I am having trouble modifying the example to point to index.html.
The example in the accepted answer suggests:
Redirect 200 /404
ErrorDocument 404 /404
which I have modified to:
Redirect 200 /index.html
ErrorDocument 404 /index.html
However this returns a standard 404 Not Found error page.
If I remove the Redirect line, leaving just the ErrorDocument line, I get the index.html page returned as required, but the https status response is still a 404 code where I need it to be a 200.
If I leave the Redirect line as per the example I actually get the same result as my modified version, so I suspect this is the line that is incorrect, but I can't figure it out.
(I'm using the Chrome Dev Tools console to see the status codes etc).
I think I have found a solution using Rewrite rules instead of error docs.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
The key I was missing in this approach seems to be not including an R=??? status response code at the end of the rewrite rule. It took me a while to find that!
As it uses mod_rewrite rather than defining error pages I assume that the mechanism is different to how CloudFront does it, but for my dev system needs it seems that the result is the same - which means I can work on the site without having to invalidate the CloudFront cache after every code change and upload.

Specify to load index.html file for every request in apache config file

I have a React application hosted on my server and I need to always load index.html file for every request users make.
Let's say that I have a website that has the address xyz.com, and the root directory contains the React build files, including this index.html file. There are many routes that users can specify to access to certain parts of the website, for example to register on the website they can access xyz.com/register. So, what I want to accomplish is instruct server to always serve this index.html every time users access my site, even though they are visiting different routes of the website.
So I'm assuming that this is something that I can set up in the .conf file for the website, and if it is, can you please let me know how I can achieve it?
You can use the below rewrite rule.
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/index.html$
RewriteRule .* /index.html [L,R=302]

Site is refusing to treat relative URLs as secure

I have a PHP app running on an Apache/RHEL setup. I have the SSL certificate installed and it all checks out okay. I am using the .htaccess file to redirect to 'https://' with this snippet
RewriteCond %{HTTPS} !^on$
RewriteRule (.*) https://example.com/$1 [R,L]
All assets on the site use relative URLs for assets, however some of them are served with 'https://' and some are served with 'http://'.
In Chrome, the stylesheets and javascripts are blocked using a relative URL. If you visit the stylesheet directly (with the https://) it loads just fine and even gives the green company name in the URL bar.
Some of the images return '302 Found' and looking at the request information via the Chrome dev tools I can see the Request Url is 'http://' while further down in the response headers section I see under location 'https://'. It seems to only do this with some images and some it doesn't - I can't find a pattern. If I visit some of the problem images directly they, again, load just fine.
Is that .htaccess redirect interfering with how the assets are served? I've restarted browsers, cleared cache, etc. I'm at a complete loss. I can provide more details about the setup if they are necessary. Thanks in advance.
edit
Just to clarify - this isn't an issue with a single absolute URL in the document giving an insecure content warning. Relative URLs are being accessed by 'http://' despite being a relative URL.

.htaccess rewrite to unmounted folder resource

Im trying to use a .htaccess file on an apache server to point /cmsfiles/flipbooks/thisFlipBook/ to /thisFlipBook
Ive tried:
RewriteRule ^/thisFlipBook/ /cmsfiles/flipbooks/thisFlipBook[L,R=302]
as well as:
RewriteRule ^/thisFlipBook/cmsfiles/flipbooks/thisFlipBook[L]
(without the R tag so that mod_rewrite knows its an internal apache redirect instead of having the browser send a new request for the rewritten resources.)
The first rule results in a 404, and the second results in:
/thisFlipBook/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/
However if you manually navigate to /cmsfiles/flipbooks/thisFlipBook it manages to find the index.html file and load it successfully.
Am i doing something wrong?
Also: server side apache redirects and unmounted folder are the correct way to be doing this right? Client started requesting so many flipbooks that we've moved them out of the project for deployments sake.

return 404 error on existing directory

Ok so i have a directory in my root folder called /pages in which i keep all my pages that i include thru index.php
What I wonder is would could i somehow return a 404 error if someone requests it? i dont like snoopy people...
Is there any way you can re-structure your app so that those pages are outside the document root? Much better answer...
Absolutely put the data outside the web root as gahooa says.
If that's totally impossible due to provider restrictions, then put a .htaccess with the following contents into the directory:
deny from all
that should block all requests to files in that directory and return a "401 forbidden".
Try this rule:
RewriteRule ^pages($|/) - [L,R=404]
But the R=404 flag does only work since Apache 2.
gahooa gives the best solution. If you can't place files outside of document root and your host runs Apache 1.3, you can also use mod_alias:
Redirect 404 /pages