I am getting ready to deploy a cakephp app onto the web and i want to move all the assets (img, js, css) to a CDN to increase performance. Is there a way to globally change the location the HTML helper links to assets instead of having to change every link.
Recently I came across this cool helper that accomplishes this task with relative ease. It's called Asset Host Helper and can be obtained from its GitHub repository.
What I liked best about it is that you don't need to worry about changing the location of the assets in your development copy (most likely on localhost) or in your production copy (on the CDN). The helper takes care of it automatically.
Check it out - this might just be the tool you're looking for.
Cheers,
m^e
If the routes and filenames persist, maybe mod_rewrite might be less painful.
RewriteCond %{REQUEST_URI} ^/css/
RewriteRule ^css/(.*)$ http://cd.yourdomain.com/css/$1 [R=301,L]
I had a similar problem, here's how I solved it:
Adding a prefix to every URL in CakePHP
The AppHelper::url() method is the place you should be interested in.
I have a solution but it involves changing the core, I know I know...I have already slapped myself for doing it ;-)
We had a project that was built and then needed a CDN so we just added a bit of code to the HTML and Javascript helpers to assist us.
In the /cake/libs/view/helpers/html.php file add this at line 360
if (Configure::read('Asset.CDN.enabled')) {
$static_servers = Configure::read('Asset.CDN.static_servers');
if(sizeof($static_servers) > 0) {
shuffle($static_servers);
$url = $static_servers[0].$url;
}
}
and in /cake/libs/view/helpers/javascript.php ass this at line 288
if (Configure::read('Asset.CDN.enabled')) {
$static_servers = Configure::read('Asset.CDN.static_servers');
if(sizeof($static_servers) > 0) {
shuffle($static_servers);
$url = $static_servers[0].$url;
}
}
Then in your app/config.core.php file just add the following configuration options
// Static File Serving on a CDN
Configure::write('Asset.CDN.enabled', false);
Configure::write('Asset.CDN.static_servers', array('http://static0.yoursite.com.au/', 'http://static1.yoursite.com.au/'));
Now when you refresh your page each file that is outputted through the html/javascript helper will automatically pick a random static server.
Note that unless you are using absolute paths (including domain names) in your css files you will need to make sure the images are also on the static server.
I know you shouldn't really play around in the core but sometimes it is really just easier.
Cheers,
Dean
I know this is an old question but in case any future people stumble across it in rails 3.1 you can now use
config.action_controller.asset_host = "ATBTracking"
in config/environments/production
Related
I'm trying to avoid cache busting by setting version numbers in the index.html file name (index.hash.html) generated with html-webpack-plugin. However, I'm unable to get the browser to grab the new file from the server because the old index.html file is still cached for X amount of time.
I could clear cache to hit the server again, or change the cache-control header but this doesn't really work well for users that already have the file cached since it seems they won't see the changes to cache-control anyway. I'm looking for the correct solution and can't seem to find one for this issue.
Any suggestions?
I'm no expert on this, but we're using no hashing for index.html. This means the TTL for it is zero.
On the other hand, all other assets ( js, css , svg ... ) have hashes defined and are cached. Our Service worker on the client checks for newer versions and serve them accordingly.
Hope this helps!
I'm pretty new with AngularJS and server config stuff, and this is a problem I haven't found a satisfactory solution so far.
I would like to be able to use the HTML5 url on a website (without hashbangs), so that I could use addresses like "mydomain/contact" to navigate (I'll stick with the "contact" example for simplicity).
To do that, as I've found so far, one should do two things:
Enable HTML5 on the client side
Enable the HTML5 format on the app/app.js file (also adding the dependency)
$locationProvider.html5Mode(true);
It makes possible to click on links and get the proper url. Still, it doesn't allow someone to access directly the HTML5 url. To get to the "contact" page, I still can't directly access "mydomain/contact" (we get a 404) and I know it makes sense. To solve this, it is still necessary to implement something server-side.
Server-side config
Configure the server to respond with the right file, i.e., I should configure the server to make it respond the same way when I request "mydomain/#/contact" and "mydomain/contact".
The last item is where I'm stuck. I've found many answers like this: "you should configure your server" (they assume the reader already knows how to do this. I don't), but I can't find a complete example on how to do that or where to put any needed files.
I'm using AngularJS 1.6.x and npm 3.10.9 (npm start). My question is: is there any complete example on how to fully use HTML5 urls with AngularJS?
The only problem that exists is that angular can't handle requests it doesn't receive. You need some catch-all so that all routes (/contact etc) are passed to your main index-file.
When you say .htaccess I assume apache. Even so I'd still put nginx in front of apache since it's lightweight and easy to configure (at least compared to the apache behemoth). Sorry, I know that is a very opinionated answer. Anyway with nginx the entire config could look like this:
# usually this file goes in /etc/nginx/conf.d/anyfilename.conf
# but it might vary with os/distro.
server {
listen 80
root /var/www/myapp;
location / {
try_files $uri $uri/ index.html;
}
# And if you want to pass some route to apache:
location /apache {
proxy_pass http://127.0.0.1:81; # Apache listening on port 81.
}
}
I'm sure the same can be achieved with apache alone, but I couldn't tell you how. But perhaps this can be of help: htaccess redirect for Angular routes
There are so many silly toolpacks and utilities I've wasted time learning in my life, but nginx is the one tool I'll never regret I picked up.
I currently have an existing Preact JS site set up but want to add blog section.
So basically easy way to add new pages and routes.
I.e. blog/content-1, blog/content-2
I know I could easily create these pages and routes manually but is there something better out there that fits into existing sites ?
I.e. a lot of the static site generators I see out there, basically you need to run from the start and they generate a new site.
But in my case I just wish to add an extra section to my site which will be the static blog entries, not sure what the best tools out there for this is ?
If any ideas or suggestions would be great.
The preactjs.com website is built exactly how you are wanting to build this. It's open source: https://github.com/developit/preact-www
Basically, fetch your HTML content via Ajax (however you'd like), then render.it as Virtual DOM using preact-markup.
For a real-world example, here's how preactjs.com does that:
https://github.com/developit/preact-www/blob/master/src/components/content-region.js
From Yahoo!'s Best Practices for Speeding Up Your Web Site document:
Expires headers are most often used with images, but they should be
used on all components including scripts, stylesheets, and Flash
components.
I follow the above advice using the "mod_expires" Apache module. My implementation is much like HTML5 Boilerplate's. See this .htaccess code.
Here is another quote from the same Yahoo! document:
Keep in mind, if you use a far future Expires header you have to
change the component's filename whenever the component changes. At
Yahoo! we often make this step part of the build process: a version
number is embedded in the component's filename, for example,
yahoo_2.0.6.js.
I have taken care of this with my CSS and JavaScript files using Mark Story's Asset Compress plugin. It's just a matter of making Asset Compress' shell part of the build process.
Now for the two issues I've run into, both related to images:
I have regular <img> tags throughout my websites and I also have CSS background-images. I currently don't have an elegant way of handling cache busting for either of those two types of images. For <img> tags, I have this line in my "core.php" file:
Configure::write('Asset.timestamp', 'force');
Although this does provide a way of automatically handling cache busting for <img> tags (provided the tags are generated using $this->Html->image(...)), I don't consider this to be elegant for two reasons:
It uses a query string, which is not recommended.
The image's timestamp is checked every time that particular view is accessed. Yes, you could cache the view, but you may want the image(s) in that view to be updated before the cached version of the view expires, so you would have to do whatever is needed to trigger that view to be re-cached, which I don't consider to be elegant.
As for handling the cache busting of CSS background-images, I have to manually update the LESS file. Definitely not elegant.
How is image caching supposed to be handled with CakePHP and/or Asset Compress?
Cache Invalidation, performance and the web
It's commonly held that one of the hardest things to do in programming is cache invalidation. However with assets (js files, css files, images etc.) that's not really true optimal logic for serving web assets is:
Serve with long cache expiry (1 year)
Don't use etags
If the asset changes change the url
There is however one complication when applied to the web.
Consider a request for /css/main.css, containing:
body {
background-image:url('../img/bar.gif');
}
This will, obviously, trigger a request for /img/bar.gif when the css file is loaded. Assuming the image is served with appropriate headers, there are only two ways to load an updated version of bar.gif:
change the contents of the css file
change the folder where the css file is
The first is problematic if it's not automated (and even if automated, could go wrong) the second is easy.
Version-prefix asset urls -> never have problems again
One simple way to solve the css/js/files problem is to make your build number part of the url:
/v123/css/foo.css
^
You can do this by modifying your app helper webroot function for example:
public function webroot($file) {
$file = parent::webroot($file);
if (Configure::read('debug')) {
return $file;
}
return '/' . Configure::read('App.version') . $file;
}
Incidentally it's the same technique to use a cdn - probably the best thing you can do to improve frontend performance.
In this way when you bump your site version - all assets get new urls. Note that using this technique, all referenced assets need to use relative urls, not absolute:
.foo {
/* background-image:url('/img/bar.gif'); // won't work */
background-image:url('../img/bar.gif');
}
Otherwise the request for the css file is application-version specific but the referenced image is not and would be read from browser cache (if relevant) even with a new application version.
Achieving the same result, no changes to file system
You can use a rewrite rule similar to the one in h5bp's for filename cache-busting if you don't want to change your folder structure:
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/v\d+/(css|files|js)/(.+)$ /$1/$2 [L]
</IfModule>
This will mean that the url /v123/css/main.css serves the contents of /css/main.css at the time of the request.
Parsing css files is complex
You mention in a comment
I think the fact that changing one asset causes all assets to be re-downloaded is a deal-breaker
Unless you are releasing a new production version every minute - it's not going to be a problem (unless you have GB of cached content in which case .. you have different problems). The only way to have performant cache logic that is file specific is to e.g. store each file in your site as the sha1 of the file's contents - applied to css files that means replacing ../img/foo.gif with ../img/<hash of foo.gif's contents>.gif.
There's nothing to stop using multiple techniques, for example with the following structure:
app
webroot
css
img <- css assets only
fonts
img
js
You can version-prefix your css, fonts and js requests; indirectly do the same for css-images (assuming they use relative urls of the form background-image:url('img/bar.gif');) without applying the same logic to other assets (user avatars, their uploaded cat videos, whatever).
Or use data uris for all images
It's what google does =).
At the end of the day it becomes a choice between how complex you want your build process to be, and for what real benefit. Many users have empty browser caches, so it's quite likely that for a random user the cache logic of an application will only apply to their current visit - one of the main reasons why expiring your whole asset cache in one go isn't such a bad thing.
I know one can host a Jekyl based static site/blog via Github pages. Can one do the same with a static site/blog based on AngularJS?
You can but you can't use html5 mode (removes the # from urls). If you use html5 mode, you have to redirect all requests to the root url since its a single page app. Since you can't use server side code on GitHub pages, you can't do this. So, if you don't mind the # in the url, go for it. If you want to use html5 mode, you need to look for hosting elsewhere.
From the Angular docs...
"Using [html5] mode requires URL rewriting on server side, basically you have to
rewrite all your links to entry point of your application (e.g. index.html)"
EDIT:
You can make use of some clever hacks to make this work if you really want to. The hacks are outlined in detail here. In summary, you rename your index.html to 404.html and github will serve it at all routes
I would say yes considering all the angular UI github pages are in fact angular apps with demos:
http://angular-ui.github.io/
http://angular-ui.github.io/bootstrap/
etc
There is one conflict between Jekyll and Angular to be aware of.
Liquid, which is included in Jekyll also uses {{ }} for evaluating expressions. To change the expressions that angular interprets (so it doesn't conflict with Liquid) use:
var myapp;
myApp = angular.module('myApp', []);
myApp.config([
'$interpolateProvider', function($interpolateProvider) {
return $interpolateProvider.startSymbol('{(').endSymbol(')}');
}
]);
Check out this blog post
Yes, you can. I recently played around with AngularJS/Typescript and github pages and was able to deploy the site.
Since AngularJS is just javascript, you can actually use any decent webserver, e.g. github pages.
Here is the demo.
You can find source code here. This repository contains typescript source code which you have to compile in order to get the appropriate javascript file. Then you basically put this include this file into your index.html and you are done.