I have a link to an mp3 on amazon s3 (the aws mp3 is set to public)
The audio player runs fine.
But when I try to make a visualizer, as soon as I connect to the audioplayer there is a CORS error. I don't understand why this should be so.
I have been using the MDN sample for analyserNode as the basis
https://developer.mozilla.org/en/docs/Web/API/AnalyserNode
<audio id="audio-player-console" src="${AWS_songUrl}" autoplay>
<p>Your browser does not support this audio player </p>
</audio>
Just to clarify, so long as no attempt to connect for analyserdata, the audioplayer runs the tracks without problem.
If I add crossorigin="anonymous" to the audio tag then I get nothing at all and the audio player won't play the track
<audio id="audio-player-console" src="${AWS_songUrl}" crossorigin="anonymous" autoplay>
<p>Your browser does not support this audio player </p>
</audio>
My AWS CORS configuration after 'make public' has been set
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Here is where I try to connect the audio tag to the audiocontext for a visualizer, which is one it starts failing the CORS check
canvasCtx = visualizerCanvas.getContext("2d")
audioCtx = new (window.AudioContext || window.webkitAudioContext)()
var audioSrc = audioCtx.createMediaElementSource(aPlayer)
var analyser = audioCtx.createAnalyser()
audioSrc.connect(analyser)
analyser.fftSize = 2048
var bufferLength = analyser.frequencyBinCount
var dataArray = new Uint8Array(bufferLength)
analyser.getByteTimeDomainData(dataArray)
I just don't know what I can do. I have set the AWS config to public, allow all origins and all headers
I eventually figured out what to do after hours and hours of research and experimenting. There is very little documentation, as of this writing, available online showing how to do this. I hope people will find this helpful.
Understanding CORS:
CORS is an acronym for Cross Origin Resoruce Sharing. CORS is a new standard for sharing/accessing information between different domains. CORS basically is a method of using server headers to tell the browser if it is permitted to access or interact with a specific file on another server. While you can load most things without worrying about CORS (like images, audio, videos, and even other web pages), interaction with these elements requires special permission from the server. In my case, I was attempting to read frequencies from an audio file on another server. In this instance, I was attempting to access information which required authorization from special headers on the server.
Browser support is very good but, if you are supporting older browsers, you may want to see support tables here (http://caniuse.com/#search=cors)
What I did:
Enabled the use of .htaccess (I think you can accomplish the same thing with apache2.conf or 000-default.conf but .htaccess files are much easier to edit and maintain). These files are used to set headers and settings for apache. I enabled the use of .htaccess files by going to /etc/apache2/ and edited apache2.conf. Make sure your entry matches the following:
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
I set the headers in my .htaccess file to allow access from Codepen. Create a .htaccess file in the same directory as the file you want to share. You only want to share what you have to or you might create a security risk. Type this in your .htaccess file:
Header set Access-Control-Allow-Origin: "http://websiteWantingToAccessYourFile.com".
Save your file.
Restart Apache with this command sudo service apache2 restart. Enter your password if prompted.
With the audio, I added the crossorigin="anonymous" attribute. You can read more about CORS settings (crossorigin) here (https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) I imagine you can set this with ajax and xhr requests.
Different versions of apache may have different file names or standards. Check to make sure this is correct for your version. I am running Apache 2.4.18 on my Ubuntu server.
Please tell me if this can be improved. I have spent a lot of time understanding this but I am not an expert. Post your questions or suggestions in the comments. :)
Related
I've started to write a HTML file which displays data with JavaScript. Since it shall be done as easy as possible I don't want to run nodejs oder any other local http server. I've just opened the HTML file in a browser (url is file:///home/visu/index.htm).
Everything is fine, till a jquery ajax request to a online API is done in the index.htm. The browser blocks the request with the message:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://x.x.x.x. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)."
How can I get rid of the problem without starting a local http server?
A possible solution is to start the browser with some "no security flags" or disable CORS with plugins, but this I've to do manually all the time so I don't like it.
When your browser will perform an AJAX request to a different server than the one hosting the current page, it first sends an OPTIONS HTTP message. In that message it sends the following header:
origin: http://my-web-server.com
And the backend server will respond with:
access-control-allow-origin: http://my-web-server.com
But, when you don't have a webserver, there is no address for your browser to put in that origin header. That's why your browser disallows you to do any AJAX request from a local file (maybe you can disable the browser's CORS security as someone mentioned in the comments, but that can put you at risk of malicious sites).
Another option
You can tell your browser to allow to connect from localhost to a backend if you change your backend to return the following header:
access-control-allow-origin: https://localhost:8888
And, you also need to tell your localhost server to serve your page in HTTPS instead of HTTP. Once both conditions are met, CORS validations won't fail.
Notice that to enable HTTPS you'll need to have a SSL cert and key, you can generate them with the following command:
openssl req -x509 -out localhost.crt -keyout localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
The source of that command and more information are found in this page from Let's Encrypt.
On Firefox, you can install this addon: https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/ to disable CORS for the respective tab. Then, any request will also work on file:/// URIs. Be careful though!
Either mock Ajax calls, or start web server with reverse proxy and HTTP rewriting configured, since I'm sure you don't want, or have not access to configure API server CORS headers.
If you don't want to mock ajax calls, then use either:
node-http-proxy
nginx - if you don't have nodejs and you don't want to install it.
Not Possible By Design
CORS are always blocked when attempted from a file on disk (web pages using the file:// protocol). There is nothing you can do to make it work from a file. It is simply impossible.
The reasoning for this is that files on disk have no real "origin" to allow the backend server to determine the validity of the request. You can have a file for an issue tracking html on the same disk as a file for a blog html. The server cannot know which html requested the data (you can even have someone else's file shared via Dropbox with embedded javascript that may attempt to access your server's data when you open it - nobody expects a hacking attempt when they simply open a plain html file!!).
This is why no browser vendor will allow you do make CORS requests from a file.
You Need a Server
To make it work you will need a HTTP server. There are lots of options for this from installing Apache/Nginx on your machine to running dev servers like webpack-dev-server or local-web-server. As long as the protocol is http:// or https:// you are allowed to make CORS requests.
Once you have a server serving your html file you can configure CORS on your backend as usual.
If you can not set it up access-control-allow-origin, you can try this.
Use "callback" function if your data is not in same domain.
And wrap your data "jsonCallback(" ... data ... ") as my example: http://www.ceducation.cz/akce-plnytext.json?short=1&callback=?
function jsonCallback(json) {
$.each(json, function(key, val) {
// your data is here
console.log('date: ' + val.date);
console.log('address: ' + val.address);
console.log('---');
});
}
$(document).ready(function() {
$.getJSON("http://www.ceducation.cz/akce-plnytext.json?short=1&callback=?", function(data) {
});
});
Working example
I am attempting to create an audio visualization program on Codepen. I have created my own Ubuntu web server with apache which allows me direct access to modify headers and configuration of the server.
While browsers can access files on a different domain, it requires special CORS headers to read the frequencies within the audio. To read audio frequencies, I must use createMediaElementSource to access audio information including the frequencies. When the browser sees this JavaScript method, it knows that there must be certain headers set on the server to allow access. Which brings us to the motives of this question: What headers need to be set?
All of the browsers I have tested return a variation of a CORS error.
This is what the error looks like in Firefox although I've tested it in Chrome, Opera, and Safari with similar results:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://williamgreen.hopto.org/audioVisualization/song.mp3. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘(null)’).
The file returns 206 partial content.
Here are the returned server headers (currently):
Here are the sent headers (currently):
function log(text) {
document.getElementById("log").textContent += text + "\n";
}
var audio, source, context;
var url = "http://williamgreen.hopto.org/audioVisualization/song.mp3";
document.addEventListener("DOMContentLoaded", function() {
log("URL: " + url);
log("Creating Audio instance from audio file");
audio = new Audio(url);
audio.crossOrigin="anonymous";
audio.addEventListener("canplay", function() {
log("Playing audio file through HTML5 Audio for 3 seconds");
audio.play();
setTimeout(function() {
log("Creating Web Audio context");
context = new (typeof AudioContext != "undefined" ? AudioContext : webkitAudioContext)();
log("Calling createMediaElementSource on audio (switching to Web Audio)");
source = context.createMediaElementSource(audio);
setTimeout(function() {
log("Connecting source to context destination");
source.connect(context.destination);
log("\nIf no sound can be heard right now, the problem was reproduced.");
}, 1000);
}, 3000);
});
});
<div id="log"></div>
What do I need to change to get this working?
My first thought is that the problem is your
Access-Control-Allow-Origin: *, *
I don't think it is understanding the *, * thing. Try just *.
Edit: you can check what the header really looks like with a command like this:
curl -v -o /dev/null http://williamgreen.hopto.org/audioVisualization/song.mp3
And, lo, it even works for me, yielding (among a lot of other headers):
< Access-Control-Allow-Origin: *
So that is hunky-dory.
Second, are you running this from a file: origin? That doesn't work on Chrome (I thought it would work on Firefox, but maybe that's changed). You have to run it from an http: or https: origin.
By "running from an file: origin" I mean, is the HTML file that is running that Javascript being loaded from an URL that beings with "file:". If so, that is not likely to work.
<script>
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.src = 'http://subdomain.domain.org:port/;stream/1';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
audio.crossorigin="anonymous";
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById('audio_box').appendChild(audio);
context = new (window.AudioContext || window.webkitAudioContext)(); // AudioContext object instance // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.crossOrigin = 'anonymous'
source.connect(analyser);
analyser.connect(context.destination);
frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper(){
(requestAnimationFrame || webkitRequestAnimationFrame)(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);//get frequency
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
ctx.fillStyle = '#00CCFF'; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
Audio API gives MediaElementAudioSource outputs zeroes due to CORS access restrictions because I'm trying to play a SHOUTcast URL. I don't know what to do; I have tried all solutions on the internet but nothing worked. Any help will be appreciated.
The URL works perfectly with audio element so its not about the URL; I have even tried something like http://subdomain.domain.org:port/file.mp3. And I found on the internet people using Icecast which is .ogg have same problem. How to fix this?
In my response I will assume the following setup:
Your stream URL is http://stream.radio.com:8000/mount (or http://stream.radio.com:8000/;stream/1 for Shoutcast)
Your paget URL where you place your HTML/JS code URL is http://radio.com/player
To get this working you need:
Set the "Access-Control-Allow-Origin" header of your stream to your domain or *
In javascript, set audio tag crossOrigin property to "anonymous" audio.crossOrigin="anonymous";
Another option it to move you stream URL to the original domain using reverse proxy.
With Icecast you cat set the "Access-Control-Allow-Origin" header using configuration file, just add the following lines to your icecast.xml, I usually add them right after the opening <icecast> tag:
<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
<header name="Access-Control-Allow-Headers" value="Origin, Accept, X-Requested-With, Content-Type, If-Modified-Since" />
<header name="Access-Control-Allow-Methods" value="GET, OPTIONS, HEAD" />
</http-headers>
Don't forget to restart Icecast after these changes. When your Icecast will be back online you can check the headers with this command:
lynx -head -dump http://stream.radio.com:8000/mount
Response should look something like this:
Server: Icecast 2.4.2
....
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, If
-Modified-Since
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
As you can see, "Access-Control-Allow-Origin: *" header is present.
Shoutcast
Unfortunately, Shoutcast does not allow you to set HTTP headers (.htaccess is not an option too), but we can create a reverse proxy in web server configuration, this will allow you to host the stream from the main domain - radio.com. I will provide proxy configurations for Nginx and Apache.
Nginx
You can add additional headers with "proxy_set_header", but the basic example is:
server {
listen 80;
server_name radio.com;
....
location /stream {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://stream.radio.com:8000/mount;
}
....
}
Apache
Activate Apache proxy modules, and update radio.com virtual host configuration configuration:
<VirtualHost *:80>
ServerName radio.com
....
ProxyPass /stream http://stream.radio.com:8000/mount
</VirtualHost>
Now you can access your stream using http://radio.com/stream URL and the CORS policy will not apply. This solution also brings some additional perks:
you can convert your http Shoutcast/Icecast stream to https, so the browsers will not complain about accessing unsecure content when you will embed your stream to the page hosted with https. (Icecast supports SSL configuration itself)
8000 port will be replaced with port 80, that will allow listeners with 8000 port behind firewall to access your stream.
That is an HTTP header. You would configure your webserver or webapp to send this header. Perhaps in htaccess or PHP. remove the below line
<header name = "Access-Control-Allow-Origin" value = "*" />
SHOUTcast servers do not support CORS. There is nothing you can do to change this if you are going to continue to use SHOUTcast.
First of all, MediaElementAudioSource doesn't have a property named "crossOrigin".
I just find this problem, and mad with the Message:MediaElementAudioSource outputs zeroes due to CORS access restrictions for. But it's just a message, i can still hear the audio.
And I googled lots of this, think this link will be helpful:http://www.codingforums.com/javascript-programming/342454-audio-api-js.html
The createMediaElementSource method should create an object that uses the MediaElementAudioSourceNode interface. Such objects are subject to Cross-Origin Resource Sharing (CORS) restrictions based on the latest draft of the Web Audio API spec. (Note that this restriction doesn't appear to be in the outdated W3C version of the spec.) According to the spec, silence should be played when CORS restrictions block access to a resource, which would explain the "outputs zeroes" message; presumably, zero is equivalent to no sound.
To lift the restriction, the owner of the page at
http://morebassradio.no-ip.org:8214/;stream/1 would need to configure
their server to output an Access-Control-Allow-Origin header with
either a list of domains (including yours) or the * value to lift it
for all domains. Given that this stream appears to already be
unrestricted, public-facing content, maybe you can convince the owners
to output that header. You can test whether the header is being sent
by pressing Ctrl+Shift+Q in Firefox to open the Network panel, loading
the stream through the address bar, and then inspecting the headers
associated with that HTTP request in the Network panel.
Note that they can't use a meta element here since the audio stream
is, obviously, not an HTML document; that technique only works for
HTML and XHTML documents.
(While you're messing with Firefox panels, you may want to make sure
Security errors and warnings are enabled (by clicking the Security
button or its arrow) in the Console panel (Ctrl+Shift+K). I'm not sure
if there's a corresponding CORS message in Firefox like in Chrome, but
there might be. I wasted a bunch of time wondering why a page wasn't
working one day while troubleshooting a similar technology, Content
Security Policy (CSP), only to find that I had the relevant Firefox
messages hidden.)
You shouldn't need to mess with the crossorigin property/attribute
unless you set crossorigin = "use-credentials" (JavaScript) or
crossorigin="use-credentials" (HTML) somewhere, but you probably
didn't do that because that part of the HTML spec isn't finalized yet,
and it would almost certainly cause your content to "break" after
doing so since credentials would be required at that point.
I'm not familiar with the Web Audio API, so I wasn't able to figure
out how to output a MediaElementAudioSourceNode and trigger an error
message for my own troubleshooting. If I use createMediaElementSource
with an HTMLMediaElement (HTMLAudioElement), the result doesn't seem
to be a MediaElementAudioSourceNode based on testing using the
instanceof operator even though the spec says it should be if I'm
reading it right.
Then in my situation, i get the HTTP response Header:
HTTP/1.1 206 Partial Content
Date: Thu, 02 Jun 2016 06:50:43 GMT
Content-Type: audio/mpeg
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Content-Disposition: inline; filename="653ab5685893b4bf.mp3"
Content-Transfer-Encoding: binary
Last-Modified: Mon, 16 May 2016 02:00:05 GMT
Server: nginx
Cache-Control: public, max-age=31536000
ETag: "FpGQqtcf_s2Ce8W_4Mv6ZqSVkVTK"
X-Log: mc.g;IO:2/304
X-Reqid: 71cAAFQgUBiJMVQU
X-Qiniu-Zone: 0
Content-Range: bytes 0-1219327/1219328
Content-Length: 1219328
Age: 1
X-Via: 1.1 xinxiazai211:88 (Cdn Cache Server V2.0), 1.1 hn13:8 (Cdn Cache Server V2.0)
Connection: keep-alive
Note that "Access-Control-Allow-Origin: *", i think this just the right thing, but i still get the message. Hope it help you.
I am attempted to use Caman.js and load images from Amazon S3. Caman.js is a JS library for producing image effects and it works by creating a copy of the image to a canvas object and doing various pixel manipulations to the data. It looks like canvas has some security details in place to limit the ability of javascript to access the pixel data when that data is coming from a foreign server unless that server passes along some security credentials in the request, or Cross-Origin Resource Sharing (CORS).
I've never encountered CORS before, am trying to learn about it, but I can't seem to get this working. From what I can understand, in order to avoid this error which appears in Chrome:
Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
Uncaught Error: SECURITY_ERR: DOM Exception 18
You need to set a CORS file on your Amazon bucket. Here's the CORS file I'm using:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
This does not seem to have any effect. Are these CORS files cached by Amazon or should I expect it to take effect immediately? -- update: I tried 8 hours later and it still is not working so I assume caching is not the issue.
It seems like the CORS file specified above should allow everything through, no? I've looked through the Chrome documentation on CORS and Google but I can't seem to find any good answers. Has anyone dealt with this issue before?
Thanks!
Kevin
Update: Here's the Response Headers I get from the image request to Amazon:
Date:Thu, 18 Oct 2012 04:52:40 GMT
ETag:"9848ce610c994521295d8aa38b47bab9"
Last-Modified:Thu, 18 Oct 2012 04:19:45 GMT
Server:AmazonS3
x-amz-id-2:Govue0tJg5MLYedr/l7T2RU5RApXLZBwJ8p507hS+sLGqxYojRnVKqj4jRHRZsZ6
x-amz-request-id:F4FF5B669C3156D2
Even though, this is 4 month old question, i would like to help others who fetched S3, CORS and HTML canvas issues by putting here a reference to a working solution.
Amazon S3 will send Cross-Origin headers only if was explicitly asked for (see CORS specs). It means that the client must ask for them explicitly in the HTTP request. The expected behavior is that the client will specify the host-name inside the "Origin: host-name" header in the request, and the server will check if it should share the resources with the specific origin name. What is important to understand here, if your client is a Web-Browser, it will not send this header for no reason.
This is the way how the Cross-Domain ajax requests works. Recently support added for some other resources like fonts and images.
Images:
HTML5 added Cross-Origin support for images in HTML tags and Javascript... Use the crossOrigin attribute on images and the browser will fetch the resource as a Cross-Origin resource (it will add the Origin header to the HTTP request) link to the original post.
The crossorigin attribute is a CORS settings attribute. Its purpose is
to allow images from third-party sites that allow cross-origin access
to be used with canvas.
read more on MDN
i also fetched similar issue, if you can rely on HTML5 support on the client side, use that solution!
I've been looking for ways of making my site load faster and one way that I'd like to explore is making greater use of Cloudfront.
Because Cloudfront was originally not designed as a custom-origin CDN and because it didn't support gzipping, I have so far been using it to host all my images, which are referenced by their Cloudfront cname in my site code, and optimized with far-futures headers.
CSS and javascript files, on the other hand, are hosted on my own server, because until now I was under the impression that they couldn't be served gzipped from Cloudfront, and that the gain from gzipping (about 75 per cent) outweighs that from using a CDN (about 50 per cent): Amazon S3 (and thus Cloudfront) did not support serving gzipped content in a standard manner by using the HTTP Accept-Encoding header that is sent by browsers to indicate their support for gzip compression, and so they were not able to Gzip and serve components on the fly.
Thus I was under the impression, until now, that one had to choose between two alternatives:
move all assets to the Amazon CloudFront and forget about GZipping;
keep components self-hosted and configure our server to detect incoming requests and perform on-the-fly GZipping as appropriate, which is what I chose to do so far.
There were workarounds to solve this issue, but essentially these didn't work. [link].
Now, it seems Amazon Cloudfront supports custom origin, and that it is now possible to use the standard HTTP Accept-Encoding method for serving gzipped content if you are using a Custom Origin [link].
I haven't so far been able to implement the new feature on my server. The blog post I linked to above, which is the only one I found detailing the change, seems to imply that you can only enable gzipping (bar workarounds, which I don't want to use), if you opt for custom origin, which I'd rather not: I find it simpler to host the coresponding fileds on my Cloudfront server, and link to them from there. Despite carefully reading the documentation, I don't know:
whether the new feature means the files should be hosted on my own domain server via custom origin, and if so, what code setup will achieve this;
how to configure the css and javascript headers to make sure they are served gzipped from Cloudfront.
UPDATE: Amazon now supports gzip compression, so this is no longer needed. Amazon Announcement
Original answer:
The answer is to gzip the CSS and JavaScript files. Yes, you read that right.
gzip -9 production.min.css
This will produce production.min.css.gz. Remove the .gz, upload to S3 (or whatever origin server you're using) and explicitly set the Content-Encoding header for the file to gzip.
It's not on-the-fly gzipping, but you could very easily wrap it up into your build/deployment scripts. The advantages are:
It requires no CPU for Apache to gzip the content when the file is requested.
The files are gzipped at the highest compression level (assuming gzip -9).
You're serving the file from a CDN.
Assuming that your CSS/JavaScript files are (a) minified and (b) large enough to justify the CPU required to decompress on the user's machine, you can get significant performance gains here.
Just remember: If you make a change to a file that is cached in CloudFront, make sure you invalidate the cache after making this type of change.
My answer is a take off on this: http://blog.kenweiner.com/2009/08/serving-gzipped-javascript-files-from.html
Building off skyler's answer you can upload a gzip and non-gzip version of the css and js. Be careful naming and test in Safari. Because safari won't handle .css.gz or .js.gz files.
site.js and site.js.jgz and
site.css and site.gz.css (you'll need to set the content-encoding header to the correct MIME type to get these to serve right)
Then in your page put.
<script type="text/javascript">var sr_gzipEnabled = false;</script>
<script type="text/javascript" src="http://d2ft4b0ve1aur1.cloudfront.net/js-050/sr.gzipcheck.js.jgz"></script>
<noscript>
<link type="text/css" rel="stylesheet" href="http://d2ft4b0ve1aur1.cloudfront.net/css-050/sr-br-min.css">
</noscript>
<script type="text/javascript">
(function () {
var sr_css_file = 'http://d2ft4b0ve1aur1.cloudfront.net/css-050/sr-br-min.css';
if (sr_gzipEnabled) {
sr_css_file = 'http://d2ft4b0ve1aur1.cloudfront.net/css-050/sr-br-min.css.gz';
}
var head = document.getElementsByTagName("head")[0];
if (head) {
var scriptStyles = document.createElement("link");
scriptStyles.rel = "stylesheet";
scriptStyles.type = "text/css";
scriptStyles.href = sr_css_file;
head.appendChild(scriptStyles);
//alert('adding css to header:'+sr_css_file);
}
}());
</script>
gzipcheck.js.jgz is just sr_gzipEnabled = true; This tests to make sure the browser can handle the gzipped code and provide a backup if they can't.
Then do something similar in the footer assuming all of your js is in one file and can go in the footer.
<div id="sr_js"></div>
<script type="text/javascript">
(function () {
var sr_js_file = 'http://d2ft4b0ve1aur1.cloudfront.net/js-050/sr-br-min.js';
if (sr_gzipEnabled) {
sr_js_file = 'http://d2ft4b0ve1aur1.cloudfront.net/js-050/sr-br-min.js.jgz';
}
var sr_script_tag = document.getElementById("sr_js");
if (sr_script_tag) {
var scriptStyles = document.createElement("script");
scriptStyles.type = "text/javascript";
scriptStyles.src = sr_js_file;
sr_script_tag.appendChild(scriptStyles);
//alert('adding js to footer:'+sr_js_file);
}
}());
</script>
UPDATE: Amazon now supports gzip compression. Announcement, so this is no longer needed. Amazon Announcement
Cloudfront supports gzipping.
Cloudfront connects to your server via HTTP 1.0. By default some webservers, including nginx, dosn't serve gzipped content to HTTP 1.0 connections, but you can tell it to do by adding:
gzip_http_version 1.0
to your nginx config. The equivalent config could be set for whichever web server you're using.
This does have a side effect of making keep-alive connections not work for HTTP 1.0 connections, but as the benefits of compression are huge, it's definitely worth the trade off.
Taken from http://www.cdnplanet.com/blog/gzip-nginx-cloudfront/
Edit
Serving content that is gzipped on the fly through Amazon cloud front is dangerous and probably shouldn't be done. Basically if your webserver is gzipping the content, it will not set a Content-Length and instead send the data as chunked.
If the connection between Cloudfront and your server is interrupted and prematurely severed, Cloudfront still caches the partial result and serves that as the cached version until it expires.
The accepted answer of gzipping it first on disk and then serving the gzipped version is a better idea as Nginx will be able to set the Content-Length header, and so Cloudfront will discard truncated versions.
We've made a few optimisations for uSwitch.com recently to compress some of the static assets on our site. Although we setup a whole nginx proxy to do this, I've also put together a little Heroku app that proxies between CloudFront and S3 to compress content: http://dfl8.co
Given publicly accessible S3 objects can be accessed using a simple URL structure, http://dfl8.co just uses the same structure. I.e. the following URLs are equivalent:
http://pingles-example.s3.amazonaws.com/sample.css
http://pingles-example.dfl8.co/sample.css
http://d1a4f3qx63eykc.cloudfront.net/sample.css
Yesterday amazon announced new feature, you can now enable gzip on your distribution.
It works with s3 without added .gz files yourself, I tried the new feature today and it works great. (need to invalidate you're current objects though)
More info
You can configure CloudFront to automatically compress files of certain types and serve the compressed files.
See AWS Developer Guide