How to set CORS headers for fonts in Phoenix? - heroku

I have a Phoenix app, that should serve its static assets (fonts, mainly) to both www.domain.com and subdomain.domain.com.
The app is hosted on heroku.
How can I set up CORS headers?
I found this library, but it doesn't seems to work on static assets (I think).
I tried to configure it like this:
defmodule MyApp.CORS do
use Corsica.Router
resource "/fonts/*", origins: ["http://subdomain.domain.com"]
end
but the resulting headers are:
cache-control:public
content-length:839
content-type:image/svg+xml
date:Sun, 19 Jun 2016 09:40:01 GMT
etag:3AAE04D
server:Cowboy

You can use the optional :headers option to Plug.Static and set the Access-Control-Allow-Origin header to *.
In lib/my_app/endpoint.ex, add the following argument at the end of the plug Plug.Static call:
headers: %{"Access-Control-Allow-Origin" => "*"}
Your code should look something like:
plug Plug.Static,
at: "/", from: :my_app, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt),
headers: %{"Access-Control-Allow-Origin" => "*"}
Note that this will not work if you want to allow more than 1 domain to work (a single domain or * would work), as I believe you have to dynamically calculate the value based on the request's Origin header for that, while Plug.Static only allows adding a static list of headers.

Related

Can't make CloudFront supply cache-control headers

I've done lots of reading on this but none of the solutions I have seen seem to work for IIS websites - most seem to suggest some server-side solution but none of that works for me.
I'm optimising one of our sites, and PageSpeed, YSlow and Lighthouse all complain that images I'm serving from our CloudFront CDN don't have any cache headers. The CDN serves from an S3 bucket.
e.g. https://static.edie.net/webimages/new_new_new.png (expiration not specified)
Crops up as both 'There are static components without a far-future expiration date' and 'Leverage browser caching for the following cacheable resources'
I can't for the life of me work out how to make CloudFront serve a cache header for images like this.
I have set
Cache-Control: max-age=5500000
on the s3 bucket/file itself, and if you check the file via the bucket: https://devedienet.s3.amazonaws.com/webimages/new_new.png then it has the cache header present.
But that doesn't seem to affect the CloudFront image, which only has these headers:
Age: 12153
Connection: keep-alive
Date: Mon, 22 Oct 2018 11:18:49 GMT
ETag: "940fd4d68428cf3e4f88a45aab4d7157"
Server: AmazonS3
Via: 1.1 4f95eb10423b781564e79d7c85f85795.cloudfront.net (CloudFront)
X-Amz-Cf-Id: TZAWy8U12-ohhe-dwTkCLqXHbJKI7CJqQd21I-lvq-8rloZjTew6aw==
x-amz-meta-s3b-last-modified: 20181017T105350Z
X-Cache: Hit from cloudfront
I've tried adding custom behavours into AWS' Control Panel for the CloudFront distribution:
webimages/*.png
Minimum TTL: 5500000
But again this seems to have no effect.
Note that I invalidated all the images in the folder after adding the new rule above, this but no dice.
Am I missing something or misunderstanding what is required?
Since you are serving content from S3 through cloudfront, then you need to add the following headers to objects in S3 while uploading files to S3. Expires: {some future date}
Bonus: You do not need to specify this header for every object individually. You can upload a bunch of files together on S3, click next, and then on the screen that asks S3 storage class, scroll down and add these headers. And don't forget to click save!
I was facing a similar problem, so after doing some reading and experimenting what I figured out is that Cloudfront's Object Caching values of Minimum TTL, Max TTL, Default TTL doesn't add the Cache-Control header explicitly in the Response Headers for the resource if the resource doesn't have one at Server level. Secondlly even if the resource has aCache-control metadata added at S3, it should fall in between
MinTTL < s3 Cache < Max TTL .
The Object caching values state that for the provided value the resources will be cached for that much time at edge location and no Cache-Control will be added in Response headers for the resource.
What I did instead was to create a Lambda function and add it under Lambda associations by updating the Cache Behaviour settings for Viewer Response. Here is my Lambda fn. This added the Cache-Control header in the requested resource.
'use strict';
exports.handler = (event, context, callback) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
const headerCache = 'Cache-Control';
headers[headerCache.toLowerCase()] = [{
key: headerCache,
value: 'max-age=1096000'
}];
callback(null, response);
};

Uploading pictures using Rack::Cors not working

I am trying to upload some pictures from my controller to my bucket on an Amazon S3. I am using the ruby Volt framework. I need CORS in order to do this, so I am using rack-cors. I have declared it correctly in my initializers/boot.rb file. This code was taken directly from the README.
Volt.current_app.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options]
end
end
Unfortunately, it does not work correctly. When I try to post a picture to my S3, I get the following error:
XMLHttpRequest cannot load https://s3.amazonaws.com/bucket-name/uploads.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:3000' is therefore not allowed access.
The response had HTTP status code 403.
Any idea as to what might be causing this?

Using Rack::Static, no Content-Type header is sent

I'm using Rack::Static (or Rack::TryStatic) to serve some static HTML, Javascript, CSS and image files as part of a Rack application, but I see that the files served by Rack::Static do not have a Content-Type header, which causes warnings in some use cases and actually breaks the behavior of other use cases.
All the files have correct extensions, but the Content-Type header is actually missing from the HTTP response. I'm running Rack using Rackup which starts a WEBRick server.
How can I get Rack::Static to set up the correct content-type when sending files?
I know this is an old post, but I just had this same problem and I hoped that the solution might help someone. Try this:
use Rack::Static, urls: {'/my-doc' => '/public/my-doc'},
header_rules: [
['/public/my-doc', {'Content-Type' => 'application/json'}]
]

Symfony2 reverse proxy - separating the caching of the same URL based on a cookie or other setting

I'm using the default Symfony2 reverse proxy, and I need to separate caching of the same URL based on a cookie setting.
The site allows for a 'basic' site view by scaling down images and removing JavaScript. As the content is the same I have used the same URL, but of course caching is an issue.
I need to be able to cache them separately (or just ensure the cache is cleared).
I have tried changing the Vary header which normally I have set to:
Vary: Accept-Encoding
..and have set it to either:
Vary: Accept-Encoding, basic
..or:
Vary: Accept-Encoding, normal
That actually works brilliant in Chrome on my Mac, but Safari ignores it. I stopped checking other browsers at this point.
What's the best way to do it?
Vary: Accept-Encoding tells the client or your reverse proxy to seperate caching of the url for different encodings. ( i.e. with/without gzip).
This is especially useful if you have older browsers not supporting gzip being served the page without gzip and for newer browsers with gzip ... so your reverse proxy will cache both variants of the same url. Without this setting your reverse proxy might end up serving gzipped content to browsers not supporting it... giving unwanted results.
What you are looking for is probably the ETag header which is kind of like a "cookie" for caching.
The client will send the etag of his cached version and you can then choose from your application wether the client's cached version is valid or not.
$response = new Response();
$response->setETag(md5('some_identifier'));
if( $response->isNotModified($this->get('request')) )
{
// automatically returns null content response with http 304 ( not modified ) header
return $response;
}
else
{
// .. otherwise return a new response, possibly with a different ETag
// $reponse->setEtag(md5('another_identifier'));
return $this->renderView('MyBundle:Main:index.html.twig', array(), $response);
}
inspired by this blogpost.

How to set HTTP response (cache) headers in a Sinatra app hosted on Heroku

I have a fairly simple app (just one index.html file and a css file - it really is just a static page) hosted on Heroku.
I use Sinatra to host it on Heroku. The 'app' itself is fairly simple:
require 'rubygems'
require 'sinatra'
get "/" do
File.read(File.join('public', 'index.html'))
end
The question is, how do I set the HTTP response header for the static assets? In particular, I wanted to set the Expires header for caching purposes.
EDIT: I'm looking to add the said header to the static assets (ie, the one that resides under /public, like background images, icons, etc)
Apart from the fact that I wouldn't get through the Sinatra stack just to serve static files, you'd call
cache_control :public, max_age: 60
to cache for a minute. cache_control is a helper that comes with Sinatra.
Otherwise, I'd suggest you have a look at http://www.sinatrarb.com/configuration.html to see how Sinatra is set up so you don't have do deal with serving static files.
Hope this helps.
edit: I just saw you were explicitly asking for the Expires header. I'm not sure, but that should be fairly the same way as Cache-Control. Sorry for the confusion
As an expansion to #awendt's answer, Sinatra can actually handle static files with out needing to explicitly define the route and print the file.
By adding:
set :static, true
..you can add your index.html and stylesheet.css to a public/ folder. Then when you visit http://localhost:9292/stylesheet.css you'll be provided with the static file.
If you want to use another folder name, instead of the default public/, then try:
set :public, "your_folder_name"
If we want to be less explicit we can just create the public/ folder in the knowledge that Sinatra will enable :static for us anyway :)
Source: http://www.sinatrarb.com/configuration.html#__enabledisable_static_file_routes

Resources